`define ysyx_040991_BR_BUS_WD       65
`define ysyx_040991_FS_TO_DS_BUS_WD 96
`define ysyx_040991_DS_TO_ES_BUS_WD 283
`define ysyx_040991_ES_TO_MS_BUS_WD 245
`define ysyx_040991_MS_TO_WS_BUS_WD 231
`define ysyx_040991_WS_TO_RF_BUS_WD 70

`define ysyx_040991_ES_FORWARDING_BUS_WD 71
`define ysyx_040991_MS_FORWARDING_BUS_WD 71
`define ysyx_040991_WS_FORWARDING_BUS_WD 70
`define ysyx_040991_CSR_FORWARDING_BUS_WD 77
`define ysyx_040991_VADDR_WIDTH 64
`define ysyx_040991_INST_WIDTH 32
`define ysyx_040991_DATA_WIDTH 64

// privilege definitions
`define ysyx_040991_EXCEPTION_WD 6
`define ysyx_040991_CSR_MSTATUS_ADDR         12'h300
`define ysyx_040991_CSR_MISA_ADDR            12'h301
`define ysyx_040991_CSR_MIE_ADDR             12'h304
`define ysyx_040991_CSR_MTVEC_ADDR           12'h305
`define ysyx_040991_CSR_MCOUNTEREN_ADDR      12'h306
`define ysyx_040991_CSR_MCOUNTINHIBIT_ADDR   12'h320

`define ysyx_040991_CSR_MSCRATCH_ADDR        12'h340
`define ysyx_040991_CSR_MEPC_ADDR            12'h341
`define ysyx_040991_CSR_MCAUSE_ADDR          12'h342
`define ysyx_040991_CSR_MTVAL_ADDR           12'h343
`define ysyx_040991_CSR_MIP_ADDR             12'h344

`define ysyx_040991_CSR_MCYCLE_ADDR          12'hB00
`define ysyx_040991_CSR_MHARTID_ADDR         12'hF14

`define ysyx_040991_PRIV_USER       2'b00
`define ysyx_040991_PRIV_SUPERVISOR 2'b11
`define ysyx_040991_PRIV_MACHINE    2'b11

`define ysyx_040991_IRQ_S_SOFT  4'd1
`define ysyx_040991_IRQ_M_SOFT  4'd3
`define ysyx_040991_IRQ_S_TIMER 4'd5
`define ysyx_040991_IRQ_M_TIMER 4'd7
`define ysyx_040991_IRQ_S_EXT   4'd9
`define ysyx_040991_IRQ_M_EXT   4'd11

`define ysyx_040991_MSTATUS_MIE 3
`define ysyx_040991_MSTATUS_MPIE 7
`define ysyx_040991_MSTATUS_MPP 12:11
`define ysyx_040991_MIP_MTIP 7
`define ysyx_040991_MIP_SD 63

`define ysyx_040991_EXCEPTION_INTERRUPT 2'b10
`define ysyx_040991_EXCEPTION_EXCEPTION 2'b01  
`define ysyx_040991_EXCEPTION_ERET      2'b11

`define ysyx_040991_EXCEPTION_INST_ADDR_MISALIGNED  4'd0
`define ysyx_040991_EXCEPTION_INST_ACCESS_FAULT     4'd1
`define ysyx_040991_EXCEPTION_ILLEGAL_INST          4'd2
`define ysyx_040991_EXCEPTION_BREAKPOINT            4'd3
`define ysyx_040991_EXCEPTION_LOAD_ADDR_MISALIGNED  4'd4
`define ysyx_040991_EXCEPTION_LOAD_ACCESS_FAULT     4'd5
`define ysyx_040991_EXCEPTION_STORE_ADDR_MISALIGNED 4'd6
`define ysyx_040991_EXCEPTION_STORE_ACCESS_FAULT    4'd7
`define ysyx_040991_EXCEPTION_ECALL_U               4'd8
`define ysyx_040991_EXCEPTION_ECALL_S               4'd9
`define ysyx_040991_EXCEPTION_ECALL_M               4'd11
`define ysyx_040991_EXCEPTION_PAGE_FAULT_INST       4'd12
`define ysyx_040991_EXCEPTION_PAGE_FAULT_LOAD       4'd13
`define ysyx_040991_EXCEPTION_PAGE_FAULT_STORE      4'd14


// cache definitions
// cache ram size ysyx_040991_CACHE_LINE_BYTE_NUM * 2^ysyx_040991_CACHE_INDEX_WD * ysyx_040991_CACHE_WAY_NUM = ysyx_040991_CACHE_DATA_RAM_SIZE 
`define ysyx_040991_CACHE_WAY_NUM 2
`define ysyx_040991_CACHE_DATA_RAM_SIZE 4096
`define ysyx_040991_CACHE_LINE_BYTE_NUM 32

`define ysyx_040991_CACHE_TAG_WD    (32 - `ysyx_040991_CACHE_INDEX_WD - `ysyx_040991_CACHE_OFFSET_WD)
`define ysyx_040991_CACHE_INDEX_WD  $clog2(`ysyx_040991_CACHE_DATA_RAM_SIZE / `ysyx_040991_CACHE_WAY_NUM / `ysyx_040991_CACHE_LINE_BYTE_NUM)
`define ysyx_040991_CACHE_OFFSET_WD $clog2(`ysyx_040991_CACHE_LINE_BYTE_NUM)

`define ysyx_040991_CACHE_LINE_WD `ysyx_040991_CACHE_LINE_BYTE_NUM * 8 
`define ysyx_040991_CACHE_DATA_RAM_BANK_NUM (`ysyx_040991_CACHE_LINE_BYTE_NUM / 8)
`define ysyx_040991_CACHE_RAM_DEPTH 2**`ysyx_040991_CACHE_INDEX_WD

`define ysyx_040991_CACHE_OP_WRITE 1'b1
`define ysyx_040991_CACHE_OP_READ 1'b0


`define ysyx_040991_ICACHE_ARID 4'h0
`define ysyx_040991_DCACHE_ARID 4'h1

`define ysyx_040991_ICACHE_RID 4'h0
`define ysyx_040991_DCACHE_RID 4'h1

`define ysyx_040991_CACHE_SYNC_BUS_WD (`ysyx_040991_CACHE_WAY_NUM * (`ysyx_040991_CACHE_LINE_WD + `ysyx_040991_CACHE_TAG_WD + 1 + 1)) // 2*(256+21+1+1)


module ysyx_040991_adder3 (
    input a,
    input b,
    input cin,
    output cout,
    output s
);
    assign s = a ^ b ^ cin;
    assign cout = (a & b) | (b & cin) | (a & cin);
endmodule


module ysyx_040991_adder #(
    parameter WIDTH = 8
)(
    input [WIDTH-1:0] a,
    input [WIDTH-1:0] b,
    output [WIDTH-1:0] y
);
    assign y = a + b;
endmodule

module ysyx_040991_alu #(
    parameter ysyx_040991_DATA_WIDTH = 64,
    parameter ALU_WIDTH = 11
)(
    input [ysyx_040991_DATA_WIDTH-1:0] alu_src1,
    input [ysyx_040991_DATA_WIDTH-1:0] alu_src2,
    input [ALU_WIDTH-1:0] alu_op,
    input word_sel, // word instructions 
    output [ysyx_040991_DATA_WIDTH-1:0] alu_result
);

    wire op_add;
    wire op_sub;
    wire op_slt;
    wire op_sltu;
    wire op_and;
    wire op_or;
    wire op_xor;
    wire op_sll;
    wire op_srl;
    wire op_sra;
    wire op_nop;

    wire op_addw = alu_op[ 0] & word_sel;
    wire op_subw = alu_op[ 1] & word_sel;
    wire op_sllw = alu_op[ 8] & word_sel;
    wire op_srlw = alu_op[ 9] & word_sel;
    wire op_sraw = alu_op[10] & word_sel;

    // control code decomposition
    assign op_add  = alu_op[ 0] & ~word_sel;
    assign op_sub  = alu_op[ 1] & ~word_sel;
    assign op_slt  = alu_op[ 2];
    assign op_sltu = alu_op[ 3];
    assign op_and  = alu_op[ 4];
    assign op_nop  = alu_op[ 5];
    assign op_or   = alu_op[ 6];
    assign op_xor  = alu_op[ 7];
    assign op_sll  = alu_op[ 8] & ~word_sel;
    assign op_srl  = alu_op[ 9] & ~word_sel;
    assign op_sra  = alu_op[10] & ~word_sel;
    

    wire [ysyx_040991_DATA_WIDTH-1:0] add_sub_result;
    wire [ysyx_040991_DATA_WIDTH-1:0] slt_result;
    wire [ysyx_040991_DATA_WIDTH-1:0] sltu_result;
    wire [ysyx_040991_DATA_WIDTH-1:0] and_result;

    wire [ysyx_040991_DATA_WIDTH-1:0] or_result;
    wire [ysyx_040991_DATA_WIDTH-1:0] xor_result;
    wire [ysyx_040991_DATA_WIDTH-1:0] nop_result;
    wire [ysyx_040991_DATA_WIDTH-1:0] sll_result;
    wire [ysyx_040991_DATA_WIDTH-1:0] srl_result;
    wire [ysyx_040991_DATA_WIDTH-1:0] sra_result;
    

    wire [ysyx_040991_DATA_WIDTH-1:0] addw_subw_result;
    wire [ysyx_040991_DATA_WIDTH-1:0] sllw_result;
    wire [ysyx_040991_DATA_WIDTH-1:0] srlw_result;
    wire [ysyx_040991_DATA_WIDTH-1:0] sraw_result; 

    wire [ysyx_040991_DATA_WIDTH-1:0] adder_a;
    wire [ysyx_040991_DATA_WIDTH-1:0] adder_b;
    wire adder_cin;
    wire [ysyx_040991_DATA_WIDTH-1:0] adder_result;
    wire adder_cout;

    assign adder_a = alu_src1;
    assign adder_b = (op_sub | op_subw | op_slt | op_sltu) ? ~alu_src2 : alu_src2;
    assign adder_cin = (op_sub | op_subw | op_slt | op_sltu) ? 1'b1 : 1'b0;
    assign {adder_cout, adder_result} = adder_a + adder_b + {63'b0, adder_cin};

    // add sub
    assign add_sub_result = adder_result;
    assign addw_subw_result = {{32{adder_result[31]}}, adder_result[31:0]};


    // slt
    assign slt_result[ysyx_040991_DATA_WIDTH-1:1] = 63'b0;
    assign slt_result[0] = (alu_src1[ysyx_040991_DATA_WIDTH-1] & ~alu_src2[ysyx_040991_DATA_WIDTH-1])
                           | (~(alu_src1[ysyx_040991_DATA_WIDTH-1] ^ alu_src2[ysyx_040991_DATA_WIDTH-1]) & adder_result[ysyx_040991_DATA_WIDTH-1]);
    
    // sltu
    assign sltu_result[ysyx_040991_DATA_WIDTH-1:1] = 63'b0;
    assign sltu_result[0] = ~adder_cout;
    
    // bitwise operations
    assign and_result = alu_src1 & alu_src2;
    assign or_result  = alu_src1 | alu_src2;

    assign xor_result = alu_src1 ^ alu_src2;
    assign nop_result = alu_src1;

    // SLL result 
    assign sll_result       = alu_src1 << alu_src2[5:0];
    wire [31:0] sllw_temp   = alu_src1[31:0] << alu_src2[4:0];
    assign sllw_result      = {{32{sllw_temp[31]}}, sllw_temp[31:0]};

    // SRL result
    assign srl_result       = alu_src1 >> alu_src2[5:0];
    wire [31:0] srlw_temp   = alu_src1[31:0] >> alu_src2[4:0];
    assign srlw_result      = {{32{srlw_temp[31]}}, srlw_temp};
    
    // SRA result
    assign sra_result       = ($signed(alu_src1)) >>> alu_src2[5:0];
    wire [31:0] sraw_temp   = ($signed(alu_src1[31:0])) >>> alu_src2[4:0];
    assign sraw_result      = {{32{sraw_temp[31]}}, sraw_temp};

    // final result mux
    assign alu_result = ({64{op_add|op_sub  }}  & add_sub_result)
                      | ({64{op_addw|op_subw}}  & addw_subw_result)
                      | ({64{op_slt         }}  & slt_result)
                      | ({64{op_sltu        }}  & sltu_result)
                      | ({64{op_and         }}  & and_result)

                      | ({64{op_or          }}  & or_result)
                      | ({64{op_xor         }}  & xor_result)
                      | ({64{op_nop         }}  & nop_result)
                      | ({64{op_sll         }}  & sll_result)
                      | ({64{op_sllw        }}  & sllw_result)
                      | ({64{op_srl         }}  & srl_result)
                      | ({64{op_srlw        }}  & srlw_result)
                      | ({64{op_sra         }}  & sra_result)
                      | ({64{op_sraw        }}  & sraw_result);
endmodule


module ysyx_040991_axi_bridge #(
    // Thread ID Width
    parameter integer C_M_AXI_ID_WIDTH	= 4,
    // Width of Address Bus
    parameter integer C_M_AXI_ADDR_WIDTH	= 32,
    // Width of Data Bus
    parameter integer C_M_AXI_ysyx_040991_DATA_WIDTH	= 64,
    // Width of User Write Address Bus
    parameter integer C_M_AXI_AWUSER_WIDTH	= 1,
    // Width of User Read Address Bus
    parameter integer C_M_AXI_ARUSER_WIDTH	= 1,
    // Width of User Write Data Bus
    parameter integer C_M_AXI_WUSER_WIDTH	= 1,
    // Width of User Read Data Bus
    parameter integer C_M_AXI_RUSER_WIDTH	= 1,
    // Width of User Response Bus
    parameter integer C_M_AXI_BUSER_WIDTH	= 1
    )(
    input M_AXI_ACLK,
    input M_AXI_ARESETN,

    // icache read inputs
    input icache_rd_req,
    input [3:0] icache_rd_type,   // 4'b1000: 8bit; 4'b0100: 16bit; 4'b0010: 32bit; 4'b0001: 64bit; 4'b0000: cache line(256bit)
    input [31:0] icache_rd_addr,  // init burst address
    output reg icache_rd_rdy,
    // return data
    output reg icache_ret_valid,
    output reg icache_ret_last,
    output reg [63:0] icache_ret_data,

    // dcache read inputs
    input dcache_rd_req,
    input [3:0] dcache_rd_type,   // 4'b1000: 8bit; 4'b0100: 16bit; 4'b0010: 32bit; 4'b0001: 64bit; 4'b0000: cache line(256bit)
    input [31:0] dcache_rd_addr,  // init burst address
    output reg dcache_rd_rdy,
    // return data
    output reg dcache_ret_valid,
    output reg dcache_ret_last,
    output reg [63:0] dcache_ret_data,
    // write request
    input dcache_wr_req,
    input [3:0] dcache_wr_type,   // 4'b1000: 8bit; 4'b0100: 16bit; 4'b0010: 32bit; 4'b0001: 64bit; 4'b0000: cache line(256bit)
    input [31:0] dcache_wr_addr,  // init burst address
    input [7:0] dcache_wr_wstrb,  // only valid when wr_type is 4'b0000 & 4'b0001 & 4'b0010 & 4'b0100
    input [255:0] dcache_wr_data,
    output dcache_wr_rdy,

    // axi4 ports
    output wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_AWID,
    output wire [C_M_AXI_ADDR_WIDTH-1 : 0] M_AXI_AWADDR,
    output wire [7 : 0] M_AXI_AWLEN,
    output wire [2 : 0] M_AXI_AWSIZE,
    output wire [1 : 0] M_AXI_AWBURST,
    output wire  M_AXI_AWLOCK,
    output wire [3 : 0] M_AXI_AWCACHE,
    output wire [2 : 0] M_AXI_AWPROT,   
    output wire [3 : 0] M_AXI_AWQOS,
    output wire [3 : 0] M_AXI_AWREGION,
    output wire [C_M_AXI_AWUSER_WIDTH-1 : 0] M_AXI_AWUSER,
    output wire  M_AXI_AWVALID,
    input wire  M_AXI_AWREADY,

    output wire [C_M_AXI_ysyx_040991_DATA_WIDTH-1 : 0] M_AXI_WDATA,
    output wire [C_M_AXI_ysyx_040991_DATA_WIDTH/8-1 : 0] M_AXI_WSTRB,
    output wire  M_AXI_WLAST,
    output wire [C_M_AXI_WUSER_WIDTH-1 : 0] M_AXI_WUSER,
    output wire  M_AXI_WVALID,              
    input wire  M_AXI_WREADY,
 
    input wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_BID,
    input wire [1 : 0] M_AXI_BRESP,
    input wire [C_M_AXI_BUSER_WIDTH-1 : 0] M_AXI_BUSER,
    input wire  M_AXI_BVALID,
    output wire  M_AXI_BREADY,

    output wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_ARID,
    output wire [C_M_AXI_ADDR_WIDTH-1 : 0] M_AXI_ARADDR,
    output wire [7 : 0] M_AXI_ARLEN,
    output wire [2 : 0] M_AXI_ARSIZE,
    output wire [1 : 0] M_AXI_ARBURST,
    output wire  M_AXI_ARLOCK,
    output wire [3 : 0] M_AXI_ARCACHE,
    output wire [2 : 0] M_AXI_ARPROT,
    output wire [3 : 0] M_AXI_ARQOS,
    output wire [3 : 0] M_AXI_ARREGION,
    output wire [C_M_AXI_ARUSER_WIDTH-1 : 0] M_AXI_ARUSER,
    output wire  M_AXI_ARVALID,
    input wire  M_AXI_ARREADY,

    input wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_RID,
    input wire [C_M_AXI_ysyx_040991_DATA_WIDTH-1 : 0] M_AXI_RDATA,
    input wire [1 : 0] M_AXI_RRESP,
    input wire  M_AXI_RLAST,
    input wire [C_M_AXI_RUSER_WIDTH-1 : 0] M_AXI_RUSER,
    input wire  M_AXI_RVALID,
    output wire M_AXI_RREADY
    );

    function integer clogb2 (input integer bit_depth);              
        begin                                                           
        for(clogb2=0; bit_depth>0; clogb2=clogb2+1)                   
            bit_depth = bit_depth >> 1;                                 
        end                                                           
    endfunction

    reg [C_M_AXI_ADDR_WIDTH-1 : 0] 	    axi_awaddr;
    reg  	                            axi_awvalid;
    reg [7 : 0]                         axi_awlen;
    reg [2 : 0]                         axi_awsize;

    reg [C_M_AXI_ysyx_040991_DATA_WIDTH-1 : 0] 	    axi_wdata;
    reg  	                            axi_wlast;
    reg  	                            axi_wvalid;
    reg [C_M_AXI_ysyx_040991_DATA_WIDTH/8-1 : 0]    axi_wstrb;

    reg  	                            axi_bready;

    reg [C_M_AXI_ADDR_WIDTH-1 : 0] 	    axi_araddr;
    reg  	                            axi_arvalid;
    reg [3 : 0]                         axi_arid;
    reg [7 : 0]                         axi_arlen;
    reg [2 : 0]                         axi_arsize;

    reg  	                            axi_rready;
    
    //write beat count in a burst
    reg [31 : 0] 	write_index;
    wire  	wnext;

    wire [7:0] C_M_AXI_BURST_LEN;
    assign C_M_AXI_BURST_LEN    = ({8{dcache_wr_type == 4'b0000}} & 8'h4) 
                                | ({8{dcache_wr_type == 4'b0001}} & 8'h1)
                                | ({8{dcache_wr_type == 4'b0010}} & 8'h1)
                                | ({8{dcache_wr_type == 4'b0100}} & 8'h1)
                                | ({8{dcache_wr_type == 4'b1000}} & 8'h1);


    assign M_AXI_AWID = 'b0;
    assign M_AXI_AWADDR	= axi_awaddr;

    assign M_AXI_AWLEN = axi_awlen;
    assign M_AXI_AWSIZE	= axi_awsize;
    assign M_AXI_AWBURST = 2'b01;
    assign M_AXI_AWLOCK	= 1'b0; 
    assign M_AXI_AWCACHE = 4'b0000; // non bufferable
    assign M_AXI_AWPROT	= 3'h0;
    assign M_AXI_AWQOS = 4'h0;
    assign M_AXI_AWREGION = 4'h0;
    assign M_AXI_AWUSER	= 'b1;
    assign M_AXI_AWVALID = axi_awvalid;
    //Write Data(W)
    assign M_AXI_WDATA = axi_wdata;
    assign M_AXI_WSTRB = axi_wstrb;
    assign M_AXI_WLAST = axi_wlast;
    assign M_AXI_WUSER = 'b0;
    assign M_AXI_WVALID	= axi_wvalid;
    //Write Response (B)
    assign M_AXI_BREADY	= axi_bready;
    //Read Address (AR)
    assign M_AXI_ARADDR	= axi_araddr;
    assign M_AXI_ARLEN = axi_arlen;
    assign M_AXI_ARSIZE = axi_arsize;
    assign M_AXI_ARBURST = 2'b01;
    assign M_AXI_ARLOCK	= 1'b0;
    assign M_AXI_ARCACHE = 4'b0000; // non bufferable
    assign M_AXI_ARPROT	= 3'h0;
    assign M_AXI_ARQOS = 4'h0;
    assign M_AXI_ARREGION = 4'h0;
    assign M_AXI_ARUSER	= 'b1;
    assign M_AXI_ARVALID = axi_arvalid;
    assign M_AXI_ARID = axi_arid;
    //Read and Read Response (R)
    assign M_AXI_RREADY	= axi_rready;
    
    assign wnext = M_AXI_WREADY & axi_wvalid;
    // write data buffer
    reg [255:0] wr_data_buffer;
    reg [31:0] wr_burst_len;
    always @(posedge M_AXI_ACLK) begin
        if (M_AXI_ARESETN == 0) begin
            wr_data_buffer <= 256'b0;
        end
        else if (dcache_wr_req) begin
            wr_data_buffer <= dcache_wr_data;
        end
        else begin
            wr_data_buffer <= wr_data_buffer;
        end
    end
    always @(posedge M_AXI_ACLK) begin
        if (M_AXI_ARESETN == 0) begin
            wr_burst_len <= 32'b0;
        end
        else if (dcache_wr_req) begin
            wr_burst_len <= {24'b0, C_M_AXI_BURST_LEN};
        end
        else begin
            wr_burst_len <= wr_burst_len;
        end
    end


    //--------------------
    //Write Address Channel
    //--------------------
    reg [2:0] aw_curr_state, aw_next_state;
    localparam AW_IDLE = 3'b001;
    localparam AW_WAIT = 3'b010;
    localparam AW_ACTIVE = 3'b100;

    assign dcache_wr_rdy = aw_curr_state == AW_WAIT || aw_curr_state == AW_IDLE;

    always @(posedge M_AXI_ACLK) begin
        if (M_AXI_ARESETN == 0) begin
            aw_curr_state <= AW_IDLE; 
        end
        else begin
            aw_curr_state <= aw_next_state;
        end
    end

    always @(*) begin
        case (aw_curr_state) 
            AW_IDLE: begin
                aw_next_state = AW_WAIT;
            end
            AW_WAIT: begin
                if (dcache_wr_req) begin
                    aw_next_state = AW_ACTIVE;
                end
                else begin
                    aw_next_state = AW_WAIT;
                end
            end
            AW_ACTIVE: begin
                if (M_AXI_BVALID && M_AXI_BREADY) begin                       
                    aw_next_state = AW_WAIT;
                end
                else begin
                    aw_next_state = AW_ACTIVE;
                end
            end
            default: begin
                aw_next_state = AW_IDLE;
            end
        endcase
    end

    always @(posedge M_AXI_ACLK) begin
        if (aw_next_state == AW_IDLE || aw_next_state == AW_WAIT) begin                                                            
            axi_awvalid <= 1'b0;
        end                                                                
        else if (aw_curr_state == AW_WAIT && dcache_wr_req) begin
            axi_awvalid <= 1'b1;
        end
        else if (M_AXI_AWREADY) begin
            axi_awvalid <= 1'b0;
        end
        else begin
            axi_awvalid <= axi_awvalid;
        end
    end                                                   

    always @(posedge M_AXI_ACLK) begin 
        if (aw_next_state == AW_IDLE || aw_next_state == AW_WAIT) begin                                                            
            axi_awaddr <= 'b0;                                             
        end                                                              
        else if (aw_curr_state == AW_WAIT && dcache_wr_req) begin                                                            
            axi_awaddr <= dcache_wr_addr;
        end                                                              
        else begin
            axi_awaddr <= axi_awaddr;  
        end                                  
    end

    always @(posedge M_AXI_ACLK) begin 
        if (aw_next_state == AW_IDLE || aw_next_state == AW_WAIT) begin                                                            
            axi_awlen <= 'b0;                                             
        end                                                              
        else if (aw_curr_state == AW_WAIT && dcache_wr_req) begin                                                            
            axi_awlen <= C_M_AXI_BURST_LEN - 8'b1;
        end                                                              
        else begin
            axi_awlen <= axi_awlen;  
        end                                  
    end
    always @(posedge M_AXI_ACLK) begin 
        if (aw_next_state == AW_IDLE || aw_next_state == AW_WAIT) begin                                                            
            axi_awsize <= 'b0;                                             
        end                                                              
        else if (aw_curr_state == AW_WAIT && dcache_wr_req) begin                                                            
            axi_awsize <= ({3{dcache_wr_type == 4'b0000}} & 3'b011) 
                        | ({3{dcache_wr_type == 4'b0001}} & 3'b011)
                        | ({3{dcache_wr_type == 4'b0010}} & 3'b010)
                        | ({3{dcache_wr_type == 4'b0100}} & 3'b001)
                        | ({3{dcache_wr_type == 4'b1000}} & 3'b000);
        end                                                              
        else begin
            axi_awsize <= axi_awsize;  
        end                                  
    end
    //--------------------
    //Write Data Channel
    //--------------------
    always @(posedge M_AXI_ACLK) begin
        if (aw_next_state == AW_IDLE || aw_next_state == AW_WAIT) begin                                                            
            axi_wvalid <= 1'b0;
        end                                                                
        else if (aw_curr_state == AW_WAIT) begin                                            
            axi_wvalid <= 1'b1;
        end                                                        
        else begin
            axi_wvalid <= axi_wvalid;
        end
    end

    always @(posedge M_AXI_ACLK) begin
        if (aw_next_state == AW_IDLE || aw_next_state == AW_WAIT) begin
            axi_wlast <= 1'b0;
        end
        else if (aw_curr_state == AW_ACTIVE && (wnext && write_index == wr_burst_len-2 || wr_burst_len == 1)) begin
            axi_wlast <= 1'b1;
        end
        else if (wnext) begin
            axi_wlast <= 1'b0;
        end
        else begin
            axi_wlast <= axi_wlast;
        end
    end

    always @(posedge M_AXI_ACLK) begin                                                                             
        if (aw_next_state == AW_IDLE || aw_next_state == AW_WAIT) begin                                                                         
            write_index <= 'b0;                                                           
        end                                                                           
        else if (wnext && (write_index != wr_burst_len-1)) begin
            write_index <= write_index + 1;    
        end                                                                           
        else begin                                                                       
            write_index <= write_index;
        end
    end

    always @(posedge M_AXI_ACLK) begin
        if (aw_next_state == AW_IDLE || aw_next_state == AW_WAIT) begin
            axi_wdata <= 64'b0;
        end
        else if (aw_curr_state == AW_WAIT && dcache_wr_req) begin
            axi_wdata <= dcache_wr_data[0 +: 64];
        end
        else if (aw_curr_state == AW_ACTIVE && wnext && write_index != wr_burst_len) begin
            axi_wdata <= wr_data_buffer[(write_index+1)*64 +: 64];
        end
        else begin
            axi_wdata <= axi_wdata;
        end
    end
    
    always @(posedge M_AXI_ACLK) begin
        if (aw_next_state == AW_IDLE || aw_next_state == AW_WAIT) begin
            axi_wstrb <= 8'b0;
        end
        else if (aw_curr_state == AW_WAIT && dcache_wr_req) begin
            axi_wstrb <= dcache_wr_wstrb;
        end
        else if (aw_curr_state == AW_ACTIVE && wnext && write_index != wr_burst_len) begin
            axi_wstrb <= 8'hff;
        end
        else begin
            axi_wstrb <= axi_wstrb;
        end
    end
    //----------------------------
    //Write Response (B) Channel
    //----------------------------
    reg [1:0] b_curr_state, b_next_state;
    localparam B_IDLE = 2'b01;
    localparam B_WAIT = 2'b10;

    always @(posedge M_AXI_ACLK) begin
        if (M_AXI_ARESETN == 0) begin
            b_curr_state <= B_IDLE; 
        end
        else begin
            b_curr_state <= b_next_state;
        end
    end

    always @(*) begin
        case (b_curr_state) 
            B_IDLE: begin
                if (dcache_wr_req) begin
                    b_next_state = B_WAIT;
                end
                else begin
                    b_next_state = B_IDLE;
                end
            end
            B_WAIT: begin
                if (M_AXI_BVALID) begin
                    b_next_state = B_IDLE;
                end
                else begin
                    b_next_state = B_WAIT;
                end
            end
            default: begin
                b_next_state = B_IDLE;
            end
        endcase
    end

    always @(*) begin
        if (b_curr_state == B_WAIT) begin
            axi_bready = 1'b1;
        end
        else begin
            axi_bready = 1'b0;
        end
    end

    //----------------------------
    //Read Address Channel
    //----------------------------

    reg [2:0] ar_curr_state, ar_next_state;
    localparam AR_IDLE = 3'b001;
    localparam AR_ICACHE = 3'b010;
    localparam AR_DCACHE = 3'b100;

    
    always @(posedge M_AXI_ACLK) begin
        if (M_AXI_ARESETN == 0) begin
            ar_curr_state <= AR_IDLE; 
        end
        else begin
            ar_curr_state <= ar_next_state;
        end
    end
    wire ar_stall;
    assign ar_stall = b_curr_state == B_WAIT;

    wire r_icache_complete, r_dcache_complete;
    always @(*) begin
        case (ar_curr_state)
            AR_IDLE: begin
                if (dcache_rd_req && !ar_stall) begin
                    ar_next_state = AR_DCACHE;
                end
                else if (icache_rd_req && !ar_stall) begin
                    ar_next_state = AR_ICACHE;
                end
                else begin
                    ar_next_state = AR_IDLE;
                end
            end
            AR_DCACHE: begin
                if (r_dcache_complete) begin
                    if (dcache_rd_req) begin
                        ar_next_state = AR_DCACHE;
                    end
                    else if (icache_rd_req) begin
                        ar_next_state = AR_ICACHE;
                    end
                    else begin
                        ar_next_state = AR_IDLE;
                    end
                end
                else begin
                    ar_next_state = AR_DCACHE;
                end
            end
            AR_ICACHE: begin
                if (r_icache_complete) begin
                    if (dcache_rd_req) begin
                        ar_next_state = AR_DCACHE;
                    end
                    else if (icache_rd_req) begin
                        ar_next_state = AR_ICACHE;
                    end
                    else begin
                        ar_next_state = AR_IDLE;
                    end
                end
                else begin
                    ar_next_state = AR_ICACHE;
                end
            end
            default: begin
                ar_next_state = AR_IDLE;
            end
        endcase
    end
    
    always @(*) begin
        case (ar_curr_state)
            AR_IDLE: begin
                axi_arid = 4'b0;
                axi_arvalid = 1'b0;
                axi_araddr = 32'b0;
                axi_arlen = 8'b0;
                axi_arsize = 3'b0;
                dcache_rd_rdy = 1'b0;
                icache_rd_rdy = 1'b0;
            end
            AR_DCACHE: begin
                axi_arid    = dcache_rd_req ? 4'b1 : 'b0;
                axi_arvalid = dcache_rd_req ? 1'b1 : 'b0;
                axi_araddr  = dcache_rd_req ? dcache_rd_addr : 'b0;
                axi_arlen   = dcache_rd_req ? ({8{dcache_rd_type == 4'b0000}} & 8'h3) 
                                            | ({8{dcache_rd_type == 4'b0001}} & 8'h0) 
                                            | ({8{dcache_rd_type == 4'b0010}} & 8'h0) 
                                            | ({8{dcache_rd_type == 4'b0100}} & 8'h0) 
                                            | ({8{dcache_rd_type == 4'b1000}} & 8'h0) 
                                            : 'b0;
                axi_arsize  = dcache_rd_req ? ({3{dcache_rd_type == 4'b0000}} & 3'b011) 
                                            | ({3{dcache_rd_type == 4'b0001}} & 3'b011) 
                                            | ({3{dcache_rd_type == 4'b0010}} & 3'b010) 
                                            | ({3{dcache_rd_type == 4'b0100}} & 3'b001) 
                                            | ({3{dcache_rd_type == 4'b1000}} & 3'b000)
                                            : 'b0;
                dcache_rd_rdy = M_AXI_ARREADY;
                icache_rd_rdy = 1'b0;
            end
            AR_ICACHE: begin
                axi_arid    = icache_rd_req ? 4'b0 : 'b0;
                axi_arvalid = icache_rd_req ? 1'b1 : 'b0;
                axi_araddr  = icache_rd_req ? icache_rd_addr : 'b0;
                axi_arlen   = icache_rd_req ? ({8{icache_rd_type == 4'b0000}} & 8'h3) 
                                            | ({8{icache_rd_type == 4'b0001}} & 8'h0) 
                                            | ({8{icache_rd_type == 4'b0010}} & 8'h0) 
                                            | ({8{icache_rd_type == 4'b0100}} & 8'h0) 
                                            | ({8{icache_rd_type == 4'b1000}} & 8'h0) 
                                            : 'b0;
                axi_arsize  = icache_rd_req ? ({3{icache_rd_type == 4'b0000}} & 3'b011) 
                                            | ({3{icache_rd_type == 4'b0001}} & 3'b011) 
                                            | ({3{icache_rd_type == 4'b0010}} & 3'b010) 
                                            | ({3{icache_rd_type == 4'b0100}} & 3'b001) 
                                            | ({3{icache_rd_type == 4'b1000}} & 3'b000) 
                                            : 'b0;
                dcache_rd_rdy = 1'b0;
                icache_rd_rdy = M_AXI_ARREADY;
            end
            default: begin
                axi_arid = 4'b0;
                axi_arvalid = 1'b0;
                axi_araddr = 32'b0;
                axi_arlen = 8'b0;
                axi_arsize = 3'b0;
                dcache_rd_rdy = 1'b0;
                icache_rd_rdy = 1'b0;
            end
        endcase
    end

    //--------------------------------
    //Read Data (and Response) Channel
    //--------------------------------
    
    assign r_icache_complete = M_AXI_RVALID && M_AXI_RLAST && axi_rready && M_AXI_RID == `ysyx_040991_ICACHE_RID;
    assign r_dcache_complete = M_AXI_RVALID && M_AXI_RLAST && axi_rready && M_AXI_RID == `ysyx_040991_DCACHE_RID;


    always @(*) begin
        if (ar_curr_state != AR_IDLE) begin
            axi_rready = 1'b1;
        end
        else begin
            axi_rready = 1'b0;
        end
    end

    reg icache_ret_valid_next;
    reg icache_ret_last_next;
    reg [63:0] icache_ret_data_next;
    reg dcache_ret_valid_next;
    reg dcache_ret_last_next;
    reg [63:0] dcache_ret_data_next; 
    always @(*) begin
        case (ar_curr_state)
            AR_IDLE: begin
                icache_ret_valid_next = 1'b0;
                icache_ret_last_next = 1'b0;
                icache_ret_data_next = 64'b0;
                dcache_ret_valid_next = 1'b0;
                dcache_ret_last_next = 1'b0;
                dcache_ret_data_next = 64'b0;
            end
            AR_ICACHE: begin
                icache_ret_valid_next = (M_AXI_RID == `ysyx_040991_ICACHE_RID) ? M_AXI_RVALID : 1'b0;
                icache_ret_last_next = (M_AXI_RID == `ysyx_040991_ICACHE_RID) ? M_AXI_RLAST : 1'b0;
                icache_ret_data_next = (M_AXI_RID == `ysyx_040991_ICACHE_RID) ? M_AXI_RDATA : 64'b0;
                dcache_ret_valid_next = 1'b0;
                dcache_ret_last_next = 1'b0;
                dcache_ret_data_next = 64'b0;
            end
            AR_DCACHE: begin
                icache_ret_valid_next = 1'b0;
                icache_ret_last_next = 1'b0;
                icache_ret_data_next = 64'b0;
                dcache_ret_valid_next = (M_AXI_RID == `ysyx_040991_DCACHE_RID) ? M_AXI_RVALID : 1'b0;
                dcache_ret_last_next = (M_AXI_RID == `ysyx_040991_DCACHE_RID) ? M_AXI_RLAST : 1'b0;
                dcache_ret_data_next = (M_AXI_RID == `ysyx_040991_DCACHE_RID) ? M_AXI_RDATA : 64'b0;
            end
            default: begin
                icache_ret_valid_next = 1'b0;
                icache_ret_last_next = 1'b0;
                icache_ret_data_next = 64'b0;
                dcache_ret_valid_next = 1'b0;
                dcache_ret_last_next = 1'b0;
                dcache_ret_data_next = 64'b0;
            end
        endcase
    end

    always @(posedge M_AXI_ACLK) begin
        if (!M_AXI_ARESETN) begin
            icache_ret_valid   <= 1'b0;
            icache_ret_last    <= 1'b0;
            icache_ret_data    <= 64'b0;
            dcache_ret_valid   <= 1'b0;
            dcache_ret_last    <= 1'b0;
            dcache_ret_data    <= 64'b0;
        end
        else begin
            icache_ret_valid   <= icache_ret_valid_next;
            icache_ret_last    <= icache_ret_last_next;
            icache_ret_data    <= icache_ret_data_next;
            dcache_ret_valid   <= dcache_ret_valid_next;
            dcache_ret_last    <= dcache_ret_last_next;
            dcache_ret_data    <= dcache_ret_data_next;
        end
    end
endmodule

module ysyx_040991_axi_clint #
(
    // Width of data bus in bits
    parameter ysyx_040991_DATA_WIDTH = 64,
    // Width of address bus in bits
    parameter ADDR_WIDTH = 32,
    // Width of wstrb (width of data bus in words)
    parameter STRB_WIDTH = (ysyx_040991_DATA_WIDTH/8),
    // Width of ID signal
    parameter ID_WIDTH = 4
)(
    input  wire                   clk,
    input  wire                   rst,
    input  wire [ID_WIDTH-1:0]    s_axi_awid,
    input  wire [ADDR_WIDTH-1:0]  s_axi_awaddr,
    input  wire [7:0]             s_axi_awlen,
    input  wire [2:0]             s_axi_awsize,
    input  wire [1:0]             s_axi_awburst,
    input  wire                   s_axi_awlock,
    input  wire [3:0]             s_axi_awcache,
    input  wire [2:0]             s_axi_awprot,
    input  wire [3:0]             s_axi_awqos,
    input  wire [3:0]             s_axi_awregion,
    input  wire [1-1:0]           s_axi_awuser,
    input  wire                   s_axi_awvalid,
    output wire                   s_axi_awready,
    input  wire [ysyx_040991_DATA_WIDTH-1:0]  s_axi_wdata,
    input  wire [STRB_WIDTH-1:0]  s_axi_wstrb,
    input  wire                   s_axi_wlast,
    input  wire [1-1:0]           s_axi_wuser,
    input  wire                   s_axi_wvalid,
    output wire                   s_axi_wready,
    output wire [ID_WIDTH-1:0]    s_axi_bid,
    output wire [1:0]             s_axi_bresp,
    output wire [1-1:0]           s_axi_buser,
    output wire                   s_axi_bvalid,
    input  wire                   s_axi_bready,
    input  wire [ID_WIDTH-1:0]    s_axi_arid,
    input  wire [ADDR_WIDTH-1:0]  s_axi_araddr,
    input  wire [7:0]             s_axi_arlen,
    input  wire [2:0]             s_axi_arsize,
    input  wire [1:0]             s_axi_arburst,
    input  wire                   s_axi_arlock,
    input  wire [3:0]             s_axi_arcache,
    input  wire [2:0]             s_axi_arprot,
    input  wire [3:0]             s_axi_arqos,
    input  wire [3:0]             s_axi_arregion,
    input  wire [1-1:0]           s_axi_aruser,
    input  wire                   s_axi_arvalid,
    output wire                   s_axi_arready,
    output wire [ID_WIDTH-1:0]    s_axi_rid,
    output wire [ysyx_040991_DATA_WIDTH-1:0]  s_axi_rdata,
    output wire [1:0]             s_axi_rresp,
    output wire                   s_axi_rlast,
    output wire [1-1:0]           s_axi_ruser,
    output wire                   s_axi_rvalid,
    input  wire                   s_axi_rready,

    output wire                   timer_interrupt
);

    parameter VALID_ADDR_WIDTH = ADDR_WIDTH - $clog2(STRB_WIDTH);
    parameter WORD_WIDTH = STRB_WIDTH;
    parameter WORD_SIZE = ysyx_040991_DATA_WIDTH/WORD_WIDTH;

    localparam [0:0]
        READ_STATE_IDLE = 1'd0,
        READ_STATE_BURST = 1'd1;

    reg [0:0] read_state_reg, read_state_next;

    localparam [1:0]
        WRITE_STATE_IDLE = 2'd0,
        WRITE_STATE_BURST = 2'd1,
        WRITE_STATE_RESP = 2'd2;

    reg [1:0] write_state_reg, write_state_next;

    reg mem_wr_en;
    reg mem_rd_en;

    reg [ID_WIDTH-1:0] read_id_reg, read_id_next;
    reg [ADDR_WIDTH-1:0] read_addr_reg, read_addr_next;
    reg [7:0] read_count_reg, read_count_next;
    reg [2:0] read_size_reg, read_size_next;
    reg [1:0] read_burst_reg, read_burst_next;
    reg [ID_WIDTH-1:0] write_id_reg, write_id_next;
    reg [ADDR_WIDTH-1:0] write_addr_reg, write_addr_next;
    reg [7:0] write_count_reg, write_count_next;
    reg [2:0] write_size_reg, write_size_next;
    reg [1:0] write_burst_reg, write_burst_next;

    reg s_axi_awready_reg, s_axi_awready_next;
    reg s_axi_wready_reg, s_axi_wready_next;
    reg [ID_WIDTH-1:0] s_axi_bid_reg, s_axi_bid_next;
    reg s_axi_bvalid_reg, s_axi_bvalid_next;
    reg s_axi_arready_reg, s_axi_arready_next;
    reg [ID_WIDTH-1:0] s_axi_rid_reg, s_axi_rid_next;
    reg [ysyx_040991_DATA_WIDTH-1:0] s_axi_rdata_reg;
    reg s_axi_rlast_reg, s_axi_rlast_next;
    reg s_axi_rvalid_reg, s_axi_rvalid_next;


    wire [2:0] unused_3 [0:1];
    wire [VALID_ADDR_WIDTH-1:0] read_addr_valid;
    assign {unused_3[0], read_addr_valid}= read_addr_reg >> (ADDR_WIDTH - VALID_ADDR_WIDTH);
    wire [VALID_ADDR_WIDTH-1:0] write_addr_valid;
    assign {unused_3[1], write_addr_valid} = write_addr_reg >> (ADDR_WIDTH - VALID_ADDR_WIDTH);

    assign s_axi_awready = s_axi_awready_reg;
    assign s_axi_wready = s_axi_wready_reg;
    assign s_axi_bid = s_axi_bid_reg;
    assign s_axi_bresp = 2'b00;
    assign s_axi_bvalid = s_axi_bvalid_reg;
    assign s_axi_arready = s_axi_arready_reg;
    assign s_axi_rid = s_axi_rid_reg;
    assign s_axi_rdata = s_axi_rdata_reg;
    assign s_axi_rresp = 2'b00;
    assign s_axi_rlast =  s_axi_rlast_reg;
    assign s_axi_rvalid = s_axi_rvalid_reg;

    integer i;

    reg [ysyx_040991_DATA_WIDTH-1:0] csr_msip;
    reg [ysyx_040991_DATA_WIDTH-1:0] csr_mtimecmp;
    reg [ysyx_040991_DATA_WIDTH-1:0] csr_mtime;

    localparam BASE_ADDR = 32'h0200_0000;
    localparam MSIP_OFFSET = 32'h0;
    localparam MTIMECMP_OFFSET = 32'h4000;
    localparam MTIME_OFFSET = 32'hBFF8;

    localparam MSIP_ADDR = BASE_ADDR + MSIP_OFFSET;
    localparam MTIMECMP_ADDR = BASE_ADDR + MTIMECMP_OFFSET;
    localparam MTIME_ADDR = BASE_ADDR + MTIME_OFFSET;

    assign timer_interrupt = (csr_mtime > csr_mtimecmp);

    always @(posedge clk) begin
        if (rst) begin
            csr_mtime <= 64'b0;
        end
        else begin
            csr_mtime <= csr_mtime + 1;
        end
    end

    wire [31:0] clog2_strb_width;
    assign clog2_strb_width = $clog2(STRB_WIDTH);

    always @(*) begin
        write_state_next = WRITE_STATE_IDLE;

        mem_wr_en = 1'b0;

        write_id_next = write_id_reg;
        write_addr_next = write_addr_reg;
        write_count_next = write_count_reg;
        write_size_next = write_size_reg;
        write_burst_next = write_burst_reg;

        s_axi_awready_next = 1'b0;
        s_axi_wready_next = 1'b0;
        s_axi_bid_next = s_axi_bid_reg;
        s_axi_bvalid_next = s_axi_bvalid_reg && !s_axi_bready;

        case (write_state_reg)
            WRITE_STATE_IDLE: begin
                s_axi_awready_next = 1'b1;

                if (s_axi_awready && s_axi_awvalid) begin
                    write_id_next = s_axi_awid;
                    write_addr_next = s_axi_awaddr;
                    write_count_next = s_axi_awlen;
                    write_size_next = s_axi_awsize < clog2_strb_width[2:0] ? s_axi_awsize : clog2_strb_width[2:0];
                    write_burst_next = s_axi_awburst;

                    s_axi_awready_next = 1'b0;
                    s_axi_wready_next = 1'b1;
                    write_state_next = WRITE_STATE_BURST;
                end else begin
                    write_state_next = WRITE_STATE_IDLE;
                end
            end
            WRITE_STATE_BURST: begin
                s_axi_wready_next = 1'b1;

                if (s_axi_wready && s_axi_wvalid) begin
                    mem_wr_en = 1'b1;
                    if (write_burst_reg != 2'b00) begin
                        write_addr_next = write_addr_reg + (1 << write_size_reg);
                    end
                    write_count_next = write_count_reg - 1;
                    if (write_count_reg > 0) begin
                        write_state_next = WRITE_STATE_BURST;
                    end else begin
                        s_axi_wready_next = 1'b0;
                        if (s_axi_bready || !s_axi_bvalid) begin
                            s_axi_bid_next = write_id_reg;
                            s_axi_bvalid_next = 1'b1;
                            s_axi_awready_next = 1'b1;
                            write_state_next = WRITE_STATE_IDLE;
                        end else begin
                            write_state_next = WRITE_STATE_RESP;
                        end
                    end
                end else begin
                    write_state_next = WRITE_STATE_BURST;
                end
            end
            WRITE_STATE_RESP: begin
                if (s_axi_bready || !s_axi_bvalid) begin
                    s_axi_bid_next = write_id_reg;
                    s_axi_bvalid_next = 1'b1;
                    s_axi_awready_next = 1'b1;
                    write_state_next = WRITE_STATE_IDLE;
                end else begin
                    write_state_next = WRITE_STATE_RESP;
                end
            end
            default: begin
                write_state_next = WRITE_STATE_IDLE;
            end
        endcase
    end

    always @(posedge clk) begin
        if (rst) begin
            write_state_reg <= WRITE_STATE_IDLE;
            s_axi_bid_reg <= {ID_WIDTH{1'b0}};
            s_axi_awready_reg <= 1'b0;
            s_axi_wready_reg <= 1'b0;
            s_axi_bvalid_reg <= 1'b0;
            csr_mtimecmp <= 64'b0;
            csr_msip <= 64'b0;

            write_id_reg <= {ID_WIDTH{1'b0}};
            write_addr_reg <= {ADDR_WIDTH{1'b0}};
            write_count_reg <= 8'd0;
            write_size_reg <= 3'd0;
            write_burst_reg <= 2'd0;
        end
        else begin
            write_state_reg <= write_state_next;

            write_id_reg <= write_id_next;
            write_addr_reg <= write_addr_next;
            write_count_reg <= write_count_next;
            write_size_reg <= write_size_next;
            write_burst_reg <= write_burst_next;

            s_axi_awready_reg <= s_axi_awready_next;
            s_axi_wready_reg <= s_axi_wready_next;
            s_axi_bid_reg <= s_axi_bid_next;
            s_axi_bvalid_reg <= s_axi_bvalid_next;

            for (i = 0; i < WORD_WIDTH; i = i + 1) begin
                if (mem_wr_en & s_axi_wstrb[i]) begin
                    case({write_addr_valid, 3'b0})
                        MSIP_ADDR: begin
                            csr_msip[WORD_SIZE*i +: WORD_SIZE] <= s_axi_wdata[WORD_SIZE*i +: WORD_SIZE];
                        end
                        MTIMECMP_ADDR: begin
                            csr_mtimecmp[WORD_SIZE*i +: WORD_SIZE] <= s_axi_wdata[WORD_SIZE*i +: WORD_SIZE];
                        end
                    endcase
                end
            end
        end
    end

    always @(*) begin
        read_state_next = READ_STATE_IDLE;

        mem_rd_en = 1'b0;

        s_axi_rid_next = s_axi_rid_reg;
        s_axi_rlast_next = s_axi_rlast_reg;
        s_axi_rvalid_next = s_axi_rvalid_reg && !s_axi_rready;

        read_id_next = read_id_reg;
        read_addr_next = read_addr_reg;
        read_count_next = read_count_reg;
        read_size_next = read_size_reg;
        read_burst_next = read_burst_reg;

        s_axi_arready_next = 1'b0;

        case (read_state_reg)
            READ_STATE_IDLE: begin
                s_axi_arready_next = 1'b1;

                if (s_axi_arready && s_axi_arvalid) begin
                    read_id_next = s_axi_arid;
                    read_addr_next = s_axi_araddr;
                    read_count_next = s_axi_arlen;
                    read_size_next = s_axi_arsize < clog2_strb_width[2:0] ? s_axi_arsize : clog2_strb_width[2:0];
                    read_burst_next = s_axi_arburst;

                    s_axi_arready_next = 1'b0;
                    read_state_next = READ_STATE_BURST;
                end else begin
                    read_state_next = READ_STATE_IDLE;
                end
            end
            READ_STATE_BURST: begin
                if (s_axi_rready || !s_axi_rvalid_reg) begin
                    mem_rd_en = 1'b1;
                    s_axi_rvalid_next = 1'b1;
                    s_axi_rid_next = read_id_reg;
                    s_axi_rlast_next = read_count_reg == 0;
                    if (read_burst_reg != 2'b00) begin
                        read_addr_next = read_addr_reg + (1 << read_size_reg);
                    end
                    read_count_next = read_count_reg - 1;
                    if (read_count_reg > 0) begin
                        read_state_next = READ_STATE_BURST;
                    end else begin
                        s_axi_arready_next = 1'b1;
                        read_state_next = READ_STATE_IDLE;
                    end
                end else begin
                    read_state_next = READ_STATE_BURST;
                end
            end
        endcase
    end

    always @(posedge clk) begin
        if (rst) begin
            read_state_reg <= READ_STATE_IDLE;
            s_axi_rlast_reg <= 1'b0;
            s_axi_rdata_reg <= {ysyx_040991_DATA_WIDTH{1'b0}};
            s_axi_rid_reg <= {ID_WIDTH{1'b0}};
            s_axi_arready_reg <= 1'b0;
            s_axi_rvalid_reg <= 1'b0;

            read_id_reg <= {ID_WIDTH{1'b0}};
            read_addr_reg <= {ADDR_WIDTH{1'b0}};
            read_count_reg <= 8'd0;
            read_size_reg <= 3'd0;
            read_burst_reg <= 2'd0;
        end
        else begin
            read_state_reg <= read_state_next;

            read_id_reg <= read_id_next;
            read_addr_reg <= read_addr_next;
            read_count_reg <= read_count_next;
            read_size_reg <= read_size_next;
            read_burst_reg <= read_burst_next;

            s_axi_arready_reg <= s_axi_arready_next;
            s_axi_rid_reg <= s_axi_rid_next;
            s_axi_rlast_reg <= s_axi_rlast_next;
            s_axi_rvalid_reg <= s_axi_rvalid_next;

            if (mem_rd_en) begin
                case({read_addr_valid, 3'b0})
                    MSIP_ADDR: begin
                        s_axi_rdata_reg <= csr_msip;
                    end
                    MTIMECMP_ADDR: begin
                        s_axi_rdata_reg <= csr_mtimecmp;
                    end
                    MTIME_ADDR: begin
                        s_axi_rdata_reg <= csr_mtime;
                    end
                endcase
            end
        end
    end
    assign s_axi_ruser = 'b0;
    assign s_axi_buser = 'b0;
endmodule
module ysyx_040991_axi_xbar #(
    // Width of data bus in bits
    parameter ysyx_040991_DATA_WIDTH = 64,
    // Width of address bus in bits
    parameter ADDR_WIDTH = 32,
    // Width of wstrb (width of data bus in words)
    parameter STRB_WIDTH = (ysyx_040991_DATA_WIDTH/8),
    // Input ID field width (from AXI masters)
    parameter S_ID_WIDTH = 4,
    // Output ID field width (towards AXI slaves)
    // Additional bits required for response routing
    parameter M_ID_WIDTH = 4,
    // Width of awuser signal
    parameter AWUSER_WIDTH = 1,
    // Width of wuser signal
    parameter WUSER_WIDTH = 1,
    // Width of buser signal
    parameter BUSER_WIDTH = 1,
    // Width of aruser signal
    parameter ARUSER_WIDTH = 1,
    // Width of ruser signal
    parameter RUSER_WIDTH = 1
    )(
    input  wire                     clk,
    input  wire                     rst,

    /*
     * AXI slave interface
     */
    output wire [M_ID_WIDTH-1:0]    io_master_awid,
    output wire [ADDR_WIDTH-1:0]    io_master_awaddr,
    output wire [7:0]               io_master_awlen,
    output wire [2:0]               io_master_awsize,
    output wire [1:0]               io_master_awburst,
    output wire                     io_master_awlock,
    output wire [3:0]               io_master_awcache,
    output wire [2:0]               io_master_awprot,
    output wire [3:0]               io_master_awqos,
    output wire [3:0]               io_master_awregion,
    output wire [AWUSER_WIDTH-1:0]  io_master_awuser,
    output wire                     io_master_awvalid,
    input  wire                     io_master_awready,
    output wire [ysyx_040991_DATA_WIDTH-1:0]    io_master_wdata,
    output wire [STRB_WIDTH-1:0]    io_master_wstrb,
    output wire                     io_master_wlast,
    output wire [WUSER_WIDTH-1:0]   io_master_wuser,
    output wire                     io_master_wvalid,
    input  wire                     io_master_wready,
    input  wire [M_ID_WIDTH-1:0]    io_master_bid,
    input  wire [1:0]               io_master_bresp,
    input  wire [BUSER_WIDTH-1:0]   io_master_buser,
    input  wire                     io_master_bvalid,
    output wire                     io_master_bready,
    output wire [S_ID_WIDTH-1:0]    io_master_arid,
    output wire [ADDR_WIDTH-1:0]    io_master_araddr,
    output wire [7:0]               io_master_arlen,
    output wire [2:0]               io_master_arsize,
    output wire [1:0]               io_master_arburst,
    output wire                     io_master_arlock,
    output wire [3:0]               io_master_arcache,
    output wire [2:0]               io_master_arprot,
    output wire [3:0]               io_master_arqos,
    output wire [3:0]               io_master_arregion,
    output wire [ARUSER_WIDTH-1:0]  io_master_aruser,
    output wire                     io_master_arvalid,
    input  wire                     io_master_arready,
    input  wire [S_ID_WIDTH-1:0]    io_master_rid,
    input  wire [ysyx_040991_DATA_WIDTH-1:0]    io_master_rdata,
    input  wire [1:0]               io_master_rresp,
    input  wire                     io_master_rlast,
    input  wire [RUSER_WIDTH-1:0]   io_master_ruser,
    input  wire                     io_master_rvalid,
    output wire                     io_master_rready,


    output wire [M_ID_WIDTH-1:0]    clint_axi_awid,
    output wire [ADDR_WIDTH-1:0]    clint_axi_awaddr,
    output wire [7:0]               clint_axi_awlen,
    output wire [2:0]               clint_axi_awsize,
    output wire [1:0]               clint_axi_awburst,
    output wire                     clint_axi_awlock,
    output wire [3:0]               clint_axi_awcache,
    output wire [2:0]               clint_axi_awprot,
    output wire [3:0]               clint_axi_awqos,
    output wire [3:0]               clint_axi_awregion,
    output wire [AWUSER_WIDTH-1:0]  clint_axi_awuser,
    output wire                     clint_axi_awvalid,
    input  wire                     clint_axi_awready,
    output wire [ysyx_040991_DATA_WIDTH-1:0]    clint_axi_wdata,
    output wire [STRB_WIDTH-1:0]    clint_axi_wstrb,
    output wire                     clint_axi_wlast,
    output wire [WUSER_WIDTH-1:0]   clint_axi_wuser,
    output wire                     clint_axi_wvalid,
    input  wire                     clint_axi_wready,
    input  wire [M_ID_WIDTH-1:0]    clint_axi_bid,
    input  wire [1:0]               clint_axi_bresp,
    input  wire [BUSER_WIDTH-1:0]   clint_axi_buser,
    input  wire                     clint_axi_bvalid,
    output wire                     clint_axi_bready,
    output wire [S_ID_WIDTH-1:0]    clint_axi_arid,
    output wire [ADDR_WIDTH-1:0]    clint_axi_araddr,
    output wire [7:0]               clint_axi_arlen,
    output wire [2:0]               clint_axi_arsize,
    output wire [1:0]               clint_axi_arburst,
    output wire                     clint_axi_arlock,
    output wire [3:0]               clint_axi_arcache,
    output wire [2:0]               clint_axi_arprot,
    output wire [3:0]               clint_axi_arqos,
    output wire [3:0]               clint_axi_arregion,
    output wire [ARUSER_WIDTH-1:0]  clint_axi_aruser,
    output wire                     clint_axi_arvalid,
    input  wire                     clint_axi_arready,
    input  wire [S_ID_WIDTH-1:0]    clint_axi_rid,
    input  wire [ysyx_040991_DATA_WIDTH-1:0]    clint_axi_rdata,
    input  wire [1:0]               clint_axi_rresp,
    input  wire                     clint_axi_rlast,
    input  wire [RUSER_WIDTH-1:0]   clint_axi_ruser,
    input  wire                     clint_axi_rvalid,
    output wire                     clint_axi_rready,
    /*
     * AXI master interface
     */
    input wire [M_ID_WIDTH-1:0]     m00_axi_awid,
    input wire [ADDR_WIDTH-1:0]     m00_axi_awaddr,
    input wire [7:0]                m00_axi_awlen,
    input wire [2:0]                m00_axi_awsize,
    input wire [1:0]                m00_axi_awburst,
    input wire                      m00_axi_awlock,
    input wire [3:0]                m00_axi_awcache,
    input wire [2:0]                m00_axi_awprot,
    input wire [3:0]                m00_axi_awqos,
    input wire [3:0]                m00_axi_awregion,
    input wire [AWUSER_WIDTH-1:0]   m00_axi_awuser,
    input wire                      m00_axi_awvalid,
    output  wire                    m00_axi_awready,
    input wire [ysyx_040991_DATA_WIDTH-1:0]     m00_axi_wdata,
    input wire [STRB_WIDTH-1:0]     m00_axi_wstrb,
    input wire                      m00_axi_wlast,
    input wire [WUSER_WIDTH-1:0]    m00_axi_wuser,
    input wire                      m00_axi_wvalid,
    output  wire                    m00_axi_wready,
    output  wire [M_ID_WIDTH-1:0]   m00_axi_bid,
    output  wire [1:0]              m00_axi_bresp,
    output  wire [BUSER_WIDTH-1:0]  m00_axi_buser,
    output  wire                    m00_axi_bvalid,
    input wire                      m00_axi_bready,
    input wire [M_ID_WIDTH-1:0]     m00_axi_arid,
    input wire [ADDR_WIDTH-1:0]     m00_axi_araddr,
    input wire [7:0]                m00_axi_arlen,
    input wire [2:0]                m00_axi_arsize,
    input wire [1:0]                m00_axi_arburst,
    input wire                      m00_axi_arlock,
    input wire [3:0]                m00_axi_arcache,
    input wire [2:0]                m00_axi_arprot,
    input wire [3:0]                m00_axi_arqos,
    input wire [3:0]                m00_axi_arregion,
    input wire [ARUSER_WIDTH-1:0]   m00_axi_aruser,
    input wire                      m00_axi_arvalid,
    output  wire                    m00_axi_arready,
    output  wire [M_ID_WIDTH-1:0]   m00_axi_rid,
    output  wire [ysyx_040991_DATA_WIDTH-1:0]   m00_axi_rdata,
    output  wire [1:0]              m00_axi_rresp,
    output  wire                    m00_axi_rlast,
    output  wire [RUSER_WIDTH-1:0]  m00_axi_ruser,
    output  wire                    m00_axi_rvalid,
    input wire                      m00_axi_rready
);
    wire mem_wen;
    wire mmio_wen;
    wire clint_wen;
    assign mem_wen = (m00_axi_awaddr & 32'hf000_0000) == 32'h8000_0000;
    assign mmio_wen = (m00_axi_awaddr & 32'hff00_0000) == 32'h3000_0000 || (m00_axi_awaddr & 32'hff00_0000) == 32'h1000_0000;
    assign clint_wen = (m00_axi_awaddr & 32'hff00_0000) == 32'h0200_0000;
    
    reg [2:0] write_channel;
    localparam IDLE_WRITE = 3'b000;
    localparam MEM_WRITE = 3'b001;
    localparam CLINT_WRITE = 3'b100;
    always @(posedge clk) begin
        if (rst) begin
            write_channel <= 3'b0;
        end
        else if (m00_axi_awvalid) begin
            if (mem_wen || mmio_wen) begin
                write_channel <= MEM_WRITE;
            end
            else if (clint_wen) begin
                write_channel <= CLINT_WRITE;
            end
            else begin
                write_channel <= IDLE_WRITE;
            end
        end
        else if (m00_axi_bvalid) begin
            write_channel <= IDLE_WRITE;
        end
    end

    // aw
    assign io_master_awid     = (mem_wen || mmio_wen) ? m00_axi_awid     : 'b0;
    assign io_master_awaddr   = (mem_wen || mmio_wen) ? m00_axi_awaddr   : 'b0;
    assign io_master_awlen    = (mem_wen || mmio_wen) ? m00_axi_awlen    : 'b0;
    assign io_master_awsize   = (mem_wen || mmio_wen) ? m00_axi_awsize   : 'b0;
    assign io_master_awburst  = (mem_wen || mmio_wen) ? m00_axi_awburst  : 'b0;
    assign io_master_awlock   = (mem_wen || mmio_wen) ? m00_axi_awlock   : 'b0;
    assign io_master_awcache  = (mem_wen || mmio_wen) ? m00_axi_awcache  : 'b0;
    assign io_master_awprot   = (mem_wen || mmio_wen) ? m00_axi_awprot   : 'b0;
    assign io_master_awqos    = (mem_wen || mmio_wen) ? m00_axi_awqos    : 'b0;
    assign io_master_awregion = (mem_wen || mmio_wen) ? m00_axi_awregion : 'b0;
    assign io_master_awuser   = (mem_wen || mmio_wen) ? m00_axi_awuser   : 'b0;
    assign io_master_awvalid  = (mem_wen || mmio_wen) ? m00_axi_awvalid  : 'b0;

    assign clint_axi_awid     = clint_wen ? m00_axi_awid     : 'b0;
    assign clint_axi_awaddr   = clint_wen ? m00_axi_awaddr   : 'b0;
    assign clint_axi_awlen    = clint_wen ? m00_axi_awlen    : 'b0;
    assign clint_axi_awsize   = clint_wen ? m00_axi_awsize   : 'b0;
    assign clint_axi_awburst  = clint_wen ? m00_axi_awburst  : 'b0;
    assign clint_axi_awlock   = clint_wen ? m00_axi_awlock   : 'b0;
    assign clint_axi_awcache  = clint_wen ? m00_axi_awcache  : 'b0;
    assign clint_axi_awprot   = clint_wen ? m00_axi_awprot   : 'b0;
    assign clint_axi_awqos    = clint_wen ? m00_axi_awqos    : 'b0;
    assign clint_axi_awregion = clint_wen ? m00_axi_awregion : 'b0;
    assign clint_axi_awuser   = clint_wen ? m00_axi_awuser   : 'b0;
    assign clint_axi_awvalid  = clint_wen ? m00_axi_awvalid  : 'b0;


    assign m00_axi_awready  = (mem_wen || mmio_wen) ? io_master_awready  :  clint_wen ? clint_axi_awready : 'b0;

    assign io_master_wstrb    = (write_channel == MEM_WRITE) ? m00_axi_wstrb  : 'b0; 
    assign io_master_wlast    = (write_channel == MEM_WRITE) ? m00_axi_wlast  : 'b0; 
    assign io_master_wuser    = (write_channel == MEM_WRITE) ? m00_axi_wuser  : 'b0; 
    assign io_master_wvalid   = (write_channel == MEM_WRITE) ? m00_axi_wvalid : 'b0; 
    assign io_master_wdata    = (write_channel == MEM_WRITE) ? m00_axi_wdata  : 'b0; 


    assign clint_axi_wstrb  = (write_channel == CLINT_WRITE) ? m00_axi_wstrb  : 'b0; 
    assign clint_axi_wlast  = (write_channel == CLINT_WRITE) ? m00_axi_wlast  : 'b0; 
    assign clint_axi_wuser  = (write_channel == CLINT_WRITE) ? m00_axi_wuser  : 'b0; 
    assign clint_axi_wvalid = (write_channel == CLINT_WRITE) ? m00_axi_wvalid : 'b0; 
    assign clint_axi_wdata  = (write_channel == CLINT_WRITE) ? m00_axi_wdata  : 'b0; 

    assign m00_axi_wready   = (write_channel == MEM_WRITE) ? io_master_wready : (write_channel == CLINT_WRITE) ? clint_axi_wready : 'b0; 

    assign m00_axi_bid      = (write_channel == MEM_WRITE) ? io_master_bid    : (write_channel == CLINT_WRITE) ? clint_axi_bid    : 'b0; 
    assign m00_axi_bresp    = (write_channel == MEM_WRITE) ? io_master_bresp  : (write_channel == CLINT_WRITE) ? clint_axi_bresp  : 'b0; 
    assign m00_axi_buser    = (write_channel == MEM_WRITE) ? io_master_buser  : (write_channel == CLINT_WRITE) ? clint_axi_buser  : 'b0; 
    assign m00_axi_bvalid   = (write_channel == MEM_WRITE) ? io_master_bvalid : (write_channel == CLINT_WRITE) ? clint_axi_bvalid : 'b0;

    assign io_master_bready  = (write_channel == MEM_WRITE) ? m00_axi_bready : 'b0; 
    assign clint_axi_bready = (write_channel == CLINT_WRITE) ? m00_axi_bready : 'b0; 

    wire mem_aren;
    wire mmio_aren;
    wire clint_aren;
    assign mem_aren = (m00_axi_araddr & 32'hf000_0000) == 32'h8000_0000;
    assign mmio_aren = (m00_axi_araddr & 32'hf000_0000) == 32'h3000_0000 || (m00_axi_araddr & 32'hf000_0000) == 32'h1000_0000;

    assign clint_aren = (m00_axi_araddr & 32'hff00_0000) == 32'h0200_0000;
    reg [2:0] read_channel;
    localparam IDLE_READ = 3'b000;
    localparam MEM_READ = 3'b001;
    localparam CLINT_READ = 3'b100;

    wire mem_rcomplete;
    wire clint_rcomplete;

    assign mem_rcomplete = io_master_rlast && io_master_rvalid && io_master_rready;
    assign clint_rcomplete = clint_axi_rlast && clint_axi_rvalid && clint_axi_rready;


    always @(posedge clk) begin
        if (rst) begin
            read_channel <= 3'b0;
        end
        else if (m00_axi_arvalid) begin
            if (mem_aren || mmio_aren) begin
                read_channel <= MEM_READ;
            end
            else if (clint_aren) begin
                read_channel <= CLINT_READ;
            end
        end
        else if (mem_rcomplete || clint_rcomplete) begin
            read_channel <= IDLE_READ;
        end
    end

    assign io_master_arid     = (mem_aren || mmio_aren) ? {3'b001, m00_axi_arid[0]}     : 'b0;
    assign io_master_araddr   = (mem_aren || mmio_aren) ? m00_axi_araddr   : 'b0;
    assign io_master_arlen    = (mem_aren || mmio_aren) ? m00_axi_arlen    : 'b0;
    assign io_master_arsize   = (mem_aren || mmio_aren) ? m00_axi_arsize   : 'b0;
    assign io_master_arburst  = (mem_aren || mmio_aren) ? m00_axi_arburst  : 'b0;
    assign io_master_arlock   = (mem_aren || mmio_aren) ? m00_axi_arlock   : 'b0;
    assign io_master_arcache  = (mem_aren || mmio_aren) ? m00_axi_arcache  : 'b0;
    assign io_master_arprot   = (mem_aren || mmio_aren) ? m00_axi_arprot   : 'b0;
    assign io_master_arqos    = (mem_aren || mmio_aren) ? m00_axi_arqos    : 'b0;
    assign io_master_arregion = (mem_aren || mmio_aren) ? m00_axi_arregion : 'b0;
    assign io_master_aruser   = (mem_aren || mmio_aren) ? m00_axi_aruser   : 'b0;
    assign io_master_arvalid  = (mem_aren || mmio_aren) ? m00_axi_arvalid  : 'b0;

    assign clint_axi_arid     = clint_aren ? {3'b100, m00_axi_arid[0]}     : 'b0;
    assign clint_axi_araddr   = clint_aren ? m00_axi_araddr   : 'b0;
    assign clint_axi_arlen    = clint_aren ? m00_axi_arlen    : 'b0;
    assign clint_axi_arsize   = clint_aren ? m00_axi_arsize   : 'b0;
    assign clint_axi_arburst  = clint_aren ? m00_axi_arburst  : 'b0;
    assign clint_axi_arlock   = clint_aren ? m00_axi_arlock   : 'b0;
    assign clint_axi_arcache  = clint_aren ? m00_axi_arcache  : 'b0;
    assign clint_axi_arprot   = clint_aren ? m00_axi_arprot   : 'b0;
    assign clint_axi_arqos    = clint_aren ? m00_axi_arqos    : 'b0;
    assign clint_axi_arregion = clint_aren ? m00_axi_arregion : 'b0;
    assign clint_axi_aruser   = clint_aren ? m00_axi_aruser   : 'b0;
    assign clint_axi_arvalid  = clint_aren ? m00_axi_arvalid  : 'b0;

    assign m00_axi_arready  = (mem_aren || mmio_aren) ? io_master_arready : clint_aren ? clint_axi_arready : 'b0;


    assign m00_axi_rid      = (read_channel == MEM_READ) ? {3'b000, io_master_rid[0]} : (read_channel == CLINT_READ) ? {3'b000, clint_axi_rid[0]} : 'b0;
    assign m00_axi_rdata    = (read_channel == MEM_READ) ? io_master_rdata            : (read_channel == CLINT_READ) ? clint_axi_rdata   : 'b0;
    assign m00_axi_rresp    = (read_channel == MEM_READ) ? io_master_rresp            : (read_channel == CLINT_READ) ? clint_axi_rresp   : 'b0;
    assign m00_axi_rlast    = (read_channel == MEM_READ) ? io_master_rlast            : (read_channel == CLINT_READ) ? clint_axi_rlast   : 'b0;
    assign m00_axi_ruser    = (read_channel == MEM_READ) ? io_master_ruser            : (read_channel == CLINT_READ) ? clint_axi_ruser   : 'b0;
    assign m00_axi_rvalid   = (read_channel == MEM_READ) ? io_master_rvalid           : (read_channel == CLINT_READ) ? clint_axi_rvalid  : 'b0;

    assign io_master_rready   = (read_channel == MEM_READ) ? m00_axi_rready  : 'b0;
    assign clint_axi_rready = (read_channel == CLINT_READ) ? m00_axi_rready  : 'b0;
    
endmodule


module ysyx_040991_BranchCondGen #(
    parameter ysyx_040991_DATA_WIDTH = 64
)(
    input [ysyx_040991_DATA_WIDTH-1:0] reg1,
    input [ysyx_040991_DATA_WIDTH-1:0] reg2,
    output br_eq,
    output br_lt,
    output br_ltu
    );

    assign br_eq = reg1 == reg2 ? 1 : 0;
    assign br_lt = $signed(reg1) < $signed (reg2) ? 1 : 0;
    assign br_ltu = reg1 < reg2 ? 1 : 0;
endmodule

module ysyx_040991_cache_core (
    input clk,
    input rst,
    // ports from cpu core
    input valid,
    input uncache, // io read or write
    input op, // 1: write, 0: read
    input [`ysyx_040991_CACHE_INDEX_WD-1:0] index, 
    input [`ysyx_040991_CACHE_TAG_WD-1:0] tag, // ppn
    input [`ysyx_040991_CACHE_OFFSET_WD-1:0] offset,
    // input [3:0] size, // 4'b1000: 8bit; 4'b0100: 16bit; 4'b0010: 32bit; 4'b0001: 64bit
    input [7:0] wstrb,
    input [63:0] wdata,

    output reg addr_ok,
    output reg data_ok,
    output reg [63:0] rdata,

    // to axi4 converter module
    // read request
    output rd_req,
    output reg [3:0] rd_type,   // 4'b1000: 8bit; 4'b0100: 16bit; 4'b0010: 32bit; 4'b0001: 64bit; 4'b0000: cache line(256bit)
    output reg [31:0] rd_addr,  // init burst address
    input rd_rdy,
    // return data
    input ret_valid,
    input ret_last,
    input [63:0] ret_data,
    // write request
    output reg wr_req,
    output reg [3:0] wr_type,   // 4'b1000: 8bit; 4'b0100: 16bit; 4'b0010: 32bit; 4'b0001: 64bit; 4'b0000: cache line(256bit)
    output reg [31:0] wr_addr,  // init burst address
    output reg [7:0] wr_wstrb,  // only valid when wr_type is 4'b0000 & 4'b0001 & 4'b0010 & 4'b0100
    output reg [255:0] wr_data, 
    input wr_rdy,

    // sync
    output  reg                         icache_sync_wready,
    input   [`ysyx_040991_CACHE_INDEX_WD-1 : 0]     icache_sync_waddr,
    input   [`ysyx_040991_CACHE_SYNC_BUS_WD-1 : 0]  icache_sync_wbus,
    input                               icache_sync_wvalid,
    
    output  reg                         dcache_sync_rready,
    input                               dcache_sync_rvalid,
    input   [`ysyx_040991_CACHE_INDEX_WD-1 : 0]     dcache_sync_raddr,
    output  [`ysyx_040991_CACHE_SYNC_BUS_WD-1 : 0]  dcache_sync_rbus,

    // data_bank0 of ram0
    input  [128 -1:0]   ram0_bank0_Q,
    output              ram0_bank0_CEN,
    output              ram0_bank0_WEN,
    output [128 -1:0]   ram0_bank0_BWEN,
    output [6   -1:0]   ram0_bank0_A,
    output [128 -1:0]   ram0_bank0_D,
    // data_bank1 of ram0
    input  [128 -1:0]   ram0_bank1_Q,
    output              ram0_bank1_CEN,
    output              ram0_bank1_WEN,
    output [128 -1:0]   ram0_bank1_BWEN,
    output [6   -1:0]   ram0_bank1_A,
    output [128 -1:0]   ram0_bank1_D,
    // data_bank0 of ram1
    input  [128 -1:0]   ram1_bank0_Q,
    output              ram1_bank0_CEN,
    output              ram1_bank0_WEN,
    output [128 -1:0]   ram1_bank0_BWEN,
    output [6   -1:0]   ram1_bank0_A,
    output [128 -1:0]   ram1_bank0_D,
    // data_bank1 of ram1
    input  [128 -1:0]   ram1_bank1_Q,
    output              ram1_bank1_CEN,
    output              ram1_bank1_WEN,
    output [128 -1:0]   ram1_bank1_BWEN,
    output [6   -1:0]   ram1_bank1_A,
    output [128 -1:0]   ram1_bank1_D
);
    // cache configuration parameters
    localparam AXI_BURST_LEN = 3;
    // main state machine control
    localparam STATE_IDLE       =   7'b0000001;
    localparam STATE_LOOKUP     =   7'b0000010;
    localparam STATE_MISS       =   7'b0000100;
    localparam STATE_REPLACE    =   7'b0001000;
    localparam STATE_REFILL     =   7'b0010000;
    localparam STATE_SYNC_RD    =   7'b0100000;
    localparam STATE_SYNC_WR    =   7'b1000000;
    reg [6:0] curr_state;
    reg [6:0] next_state;

    localparam WB_STATE_IDLE = 1'b0;
    localparam WB_STATE_WRITE = 1'b1;
    reg wb_curr_state;
    reg wb_next_state;

    always @(posedge clk) begin
        if (rst) begin
            curr_state <= STATE_IDLE;
        end
        else begin
            curr_state <= next_state;
        end
    end
    
    // rams
    // tagv ram 
    wire way0_tagv_wen, way1_tagv_wen;
    wire way0_tagv_cen, way1_tagv_cen;
    wire [`ysyx_040991_CACHE_TAG_WD:0] way0_tagv_din, way1_tagv_din, tagv_din;
    wire [`ysyx_040991_CACHE_INDEX_WD-1:0] tagv_addr;
    wire way0_v_dout, way1_v_dout;
    wire [`ysyx_040991_CACHE_TAG_WD-1:0] way0_tag_dout, way1_tag_dout;
    ysyx_040991_tag_v_ram u_tag_v_ram0(
    	.clk  (clk                  ),
        .rst  (rst                  ),
        .cen  (way0_tagv_cen        ),
        .addr (tagv_addr            ),
        .din  (way0_tagv_din        ),
        .wen  (way0_tagv_wen        ),
        .dout ({way0_tag_dout, way0_v_dout}   )
    );
    ysyx_040991_tag_v_ram u_tag_v_ram1(
    	.clk  (clk                  ),
        .rst  (rst                  ),
        .cen  (way1_tagv_cen        ),
        .addr (tagv_addr            ),
        .din  (way1_tagv_din        ),
        .wen  (way1_tagv_wen        ),
        .dout ({way1_tag_dout, way1_v_dout}   )
    );
    // dirty bit ram
    wire way0_d_cen, way1_d_cen;
    wire way0_d_wen, way1_d_wen;
    wire way0_d_din, way1_d_din, d_din;
    wire [`ysyx_040991_CACHE_INDEX_WD-1:0] d_addr;
    wire way0_d_dout, way1_d_dout;
    ysyx_040991_cache_d_ram u_cache_d_ram0(
    	.clk  (clk          ),
        .rst  (rst          ),
        .cen  (way0_d_cen   ),
        .addr (d_addr       ),
        .din  (way0_d_din        ),
        .wen  (way0_d_wen   ),
        .dout (way0_d_dout  )
    );
    ysyx_040991_cache_d_ram u_cache_d_ram1(
    	.clk  (clk          ),
        .rst  (rst          ),
        .cen  (way1_d_cen   ),
        .addr (d_addr       ),
        .din  (way1_d_din        ),
        .wen  (way1_d_wen   ),
        .dout (way1_d_dout  )
    );
    // data ram
    wire way0_data_cen, way1_data_cen;
    wire way0_data_wen, way1_data_wen;
    wire [`ysyx_040991_CACHE_INDEX_WD-1:0] data_raddr;
    wire [`ysyx_040991_CACHE_INDEX_WD-1:0] data_waddr;
    wire [`ysyx_040991_CACHE_LINE_WD-1:0] way0_data_din, way1_data_din, data_din;
    wire [1:0] data_rbank;
    wire [3:0] data_wbank;
    wire [7:0] data_wstrb;
    wire [`ysyx_040991_CACHE_LINE_WD-1:0] way0_data_dout, way1_data_dout;

    ysyx_040991_cache_data_ram u_cache_data_ram0(
        .cen        (way0_data_cen         ),
        .raddr      (data_raddr            ),
        .waddr      (data_waddr            ),
        .din        (way0_data_din         ),
        .wen        (way0_data_wen         ),
        .rbank      (data_rbank            ),
        .wbank      (data_wbank            ),
        .wmask      (data_wstrb            ),
        .dout       (way0_data_dout        ),

        .bank0_Q    (ram0_bank0_Q          ),
        .bank0_CEN  (ram0_bank0_CEN        ),
        .bank0_WEN  (ram0_bank0_WEN        ),
        .bank0_BWEN (ram0_bank0_BWEN       ),
        .bank0_A    (ram0_bank0_A          ),
        .bank0_D    (ram0_bank0_D          ),

        .bank1_Q    (ram0_bank1_Q          ),
        .bank1_CEN  (ram0_bank1_CEN        ),
        .bank1_WEN  (ram0_bank1_WEN        ),
        .bank1_BWEN (ram0_bank1_BWEN       ),
        .bank1_A    (ram0_bank1_A          ),
        .bank1_D    (ram0_bank1_D          ) 
    );
    ysyx_040991_cache_data_ram u_cache_data_ram1(
        .cen        (way1_data_cen         ),
        .raddr      (data_raddr            ),
        .waddr      (data_waddr            ),
        .din        (way1_data_din         ),
        .wen        (way1_data_wen         ),
        .rbank      (data_rbank            ),
        .wbank      (data_wbank            ),
        .wmask      (data_wstrb            ),
        .dout       (way1_data_dout        ),

        .bank0_Q    (ram1_bank0_Q          ),
        .bank0_CEN  (ram1_bank0_CEN        ),
        .bank0_WEN  (ram1_bank0_WEN        ),
        .bank0_BWEN (ram1_bank0_BWEN       ),
        .bank0_A    (ram1_bank0_A          ),
        .bank0_D    (ram1_bank0_D          ),

        .bank1_Q    (ram1_bank1_Q          ),
        .bank1_CEN  (ram1_bank1_CEN        ),
        .bank1_WEN  (ram1_bank1_WEN        ),
        .bank1_BWEN (ram1_bank1_BWEN       ),
        .bank1_A    (ram1_bank1_A          ),
        .bank1_D    (ram1_bank1_D          ) 
    );

    // data path
    // request buffer
    reg                             op_r    ; 
    reg [`ysyx_040991_CACHE_INDEX_WD    -1:0]   index_r ;
    reg [`ysyx_040991_CACHE_TAG_WD      -1:0]   tag_r   ;
    reg [`ysyx_040991_CACHE_OFFSET_WD   -1:0]   offset_r;
    reg [7:0]                       wstrb_r ;
    reg [63:0]                      wdata_r ;
    reg                             uncache_r;
    wire rb_en; // request buffer change
    always @(posedge clk) begin
        if (rst) begin
            op_r        <=  'b0;
            index_r     <=  'b0;
            tag_r       <=  'b0;
            offset_r    <=  'b0;
            wstrb_r     <=  'b0;
            wdata_r     <=  'b0;
            uncache_r   <=  'b0;
        end
        else if (rb_en) begin
            op_r        <=  op;
            index_r     <=  index;
            tag_r       <=  tag;
            offset_r    <=  offset;
            wstrb_r     <=  wstrb;
            wdata_r     <=  wdata;
            uncache_r   <=  uncache;
        end
        else begin
            op_r        <=  op_r;
            index_r     <=  index_r;
            tag_r       <=  tag_r;
            offset_r    <=  offset_r;
            wstrb_r     <=  wstrb_r;
            wdata_r     <=  wdata_r;
            uncache_r   <=  uncache_r;
        end
    end

    // tag compare
    wire way0_hit, way1_hit;
    wire cache_hit;
    assign way0_hit = way0_v_dout && (way0_tag_dout == tag_r);
    assign way1_hit = way1_v_dout && (way1_tag_dout == tag_r);
    assign cache_hit = way0_hit || way1_hit;

    // data select
    wire [63:0] way0_load_data, way1_load_data;
    wire [63:0] load_res;
    assign way0_load_data = way0_data_dout[offset_r[4:3]*64 +: 64]; // offset from physical address
    assign way1_load_data = way1_data_dout[offset_r[4:3]*64 +: 64];
    assign load_res = ({64{way0_hit}} & way0_load_data)
                    | ({64{way1_hit}} & way1_load_data); 

    wire [`ysyx_040991_CACHE_LINE_WD-1:0] replace_data;
    wire replace_way;

    
    // control signals 
    wire hit_write;
    assign hit_write = (curr_state == STATE_LOOKUP) && (op_r == `ysyx_040991_CACHE_OP_WRITE) && cache_hit;
    // hit write hazard
    reg hazard_case0;
    wire hazard_case1;
    wire is_dirty;

    // LRU record
    reg way0_age;
    reg way1_age;
    always @(posedge clk) begin
        if (rst) begin
            way0_age <= 1'b1;
            way1_age <= 1'b0;
        end
        else if (way0_hit) begin
            way0_age <= 1'b1;
            way1_age <= 1'b0;
        end
        else if (way1_hit) begin
            way0_age <= 1'b0;
            way1_age <= 1'b1;
        end
    end
    assign replace_way  = ((way0_age == 1'b0) & 1'b0)
                        | ((way1_age == 1'b0) & 1'b1);

    // miss buffer
    reg mb_replace_way;
    reg [1:0] mb_rbeat_cnt;
    always @(posedge clk) begin
        if (rst) begin
            mb_replace_way <= 1'b0;
        end
        else if (curr_state == STATE_MISS && next_state == STATE_REPLACE) begin
            mb_replace_way <= replace_way;
        end
        else begin
            mb_replace_way <= mb_replace_way;
        end
    end

    wire rnext;
    assign rnext = ret_valid;
    always @(posedge clk) begin
        if (rst || (curr_state == STATE_REPLACE && next_state == STATE_REFILL)) begin
            mb_rbeat_cnt <= 2'b0;
        end
        else if (rnext && (mb_rbeat_cnt != AXI_BURST_LEN)) begin
            mb_rbeat_cnt <= mb_rbeat_cnt + 1'b1;
        end
        else begin
            mb_rbeat_cnt <= mb_rbeat_cnt;
        end
    end

    // write buffer

    reg wb_valid;
    reg wb_way_id;
    reg [1:0] wb_bank_id;
    reg [`ysyx_040991_CACHE_INDEX_WD-1:0] wb_index;
    reg [7:0] wb_bank_wstrb;
    reg [63:0] wb_wdata;
    always @(posedge clk) begin
        if (rst) begin
            wb_valid        <=  1'b0;
            wb_way_id       <=  1'b0;
            wb_bank_id      <=  2'b0;
            wb_index        <=  {`ysyx_040991_CACHE_INDEX_WD{1'b0}};
            wb_bank_wstrb   <=  8'b0;
            wb_wdata        <=  64'b0;
        end
        else if (hit_write) begin
            wb_valid        <=  1'b1;
            wb_way_id       <=  (way0_hit & 1'b0) | (way1_hit & 1'b1);
            wb_bank_id      <=  offset_r[4:3];
            wb_index        <=  index_r;
            wb_bank_wstrb   <=  wstrb_r;
            wb_wdata        <=  wdata_r;
        end
        else begin
            wb_valid        <=  1'b0;
            wb_way_id       <=  1'b0;
            wb_bank_id      <=  2'b0;
            wb_index        <=  {`ysyx_040991_CACHE_INDEX_WD{1'b0}};
            wb_bank_wstrb   <=  8'b0;
            wb_wdata        <=  64'b0;
        end
    end
    // control path
    // write buffer state machine

    always @(posedge clk) begin
        if (rst) begin
            wb_curr_state <= WB_STATE_IDLE;
        end
        else begin
            wb_curr_state <= wb_next_state;
        end
    end

    always @(*) begin
        case (wb_curr_state)
            WB_STATE_IDLE: begin
                if (hit_write) begin
                    wb_next_state = WB_STATE_WRITE;
                end
                else begin
                    wb_next_state = WB_STATE_IDLE;
                end
            end
            WB_STATE_WRITE: begin
                if (hit_write && wb_valid) begin
                    wb_next_state = WB_STATE_WRITE;
                end
                else if (!hit_write && wb_valid) begin
                    wb_next_state = WB_STATE_IDLE;
                end
                else begin
                    wb_next_state = WB_STATE_WRITE;
                end
            end
            default: begin
                wb_next_state = WB_STATE_IDLE;
            end
        endcase
    end

    always @(*) begin
        case (curr_state)
            STATE_IDLE: begin
                if (dcache_sync_rvalid) begin
                    next_state = STATE_SYNC_RD;
                end
                else if (icache_sync_wvalid) begin
                    next_state = STATE_SYNC_WR;
                end
                else if(valid) begin
                    next_state = STATE_LOOKUP;
                end
                else if (!valid) begin
                    next_state = STATE_IDLE;
                end
                else begin
                    next_state = STATE_IDLE;
                end
            end
            STATE_LOOKUP: begin
                if (!cache_hit || uncache_r) begin
                    next_state = STATE_MISS;
                end
                else if ((!valid && cache_hit) || hazard_case1) begin 
                    next_state = STATE_IDLE;
                end
                else if (valid && cache_hit) begin
                    next_state = STATE_LOOKUP;
                end
                else begin
                    next_state = STATE_IDLE;
                end
            end
            STATE_MISS: begin
                if (!wr_rdy) begin
                    next_state = STATE_MISS;
                end
                else begin
                    next_state = STATE_REPLACE;
                end
            end
            STATE_REPLACE: begin
                if ((uncache_r && op_r == `ysyx_040991_CACHE_OP_WRITE) || rd_rdy)begin
                    next_state = STATE_REFILL;
                end
                else if (!rd_rdy) begin
                    next_state = STATE_REPLACE;
                end
                else begin
                    next_state = STATE_IDLE;
                end
            end
            STATE_REFILL: begin
                if((ret_valid && ret_last) || (uncache_r && op_r == `ysyx_040991_CACHE_OP_WRITE)) begin
                    next_state = STATE_IDLE;
                end
                else begin
                    next_state = STATE_REFILL;
                end
            end
            STATE_SYNC_RD: begin
                if (!dcache_sync_rvalid) begin
                    next_state = STATE_IDLE;
                end
                else begin
                    next_state = STATE_SYNC_RD;
                end
            end
            STATE_SYNC_WR: begin
                if (!icache_sync_wvalid) begin
                    next_state = STATE_IDLE;
                end
                else begin
                    next_state = STATE_SYNC_WR;
                end
            end
            default: begin
                next_state = STATE_IDLE;
            end
        endcase
    end
    
    // case 0
    // bypass rdata
    always @(posedge clk) begin
        if (rst) begin
            hazard_case0 <= 1'b0;
        end
        else begin
            hazard_case0 <= hit_write && valid && (op == `ysyx_040991_CACHE_OP_READ) && ({tag, index, offset} == {tag_r, index_r, offset_r});
        end
    end
    // case 1
    // stall main state machine idle
    assign hazard_case1 = (wb_curr_state == WB_STATE_WRITE) && valid && (offset[4] == wb_bank_id[1]);
    assign rb_en = next_state == STATE_LOOKUP;
    assign is_dirty = (!mb_replace_way & (way0_d_dout && way0_v_dout)) | (mb_replace_way & (way1_d_dout && way1_v_dout));

    // sync data to be written
    wire [`ysyx_040991_CACHE_TAG_WD:0] way0_sync_tagv;
    wire [`ysyx_040991_CACHE_TAG_WD:0] way1_sync_tagv;
    wire way0_sync_d;
    wire way1_sync_d;
    wire [`ysyx_040991_CACHE_LINE_WD-1:0] way0_sync_data;
    wire [`ysyx_040991_CACHE_LINE_WD-1:0] way1_sync_data;

    
    assign way0_sync_tagv   = icache_sync_wbus[257+:22];
    assign way1_sync_tagv   = icache_sync_wbus[536+:22];
    assign way0_sync_d      = icache_sync_wbus[256+:1];
    assign way1_sync_d      = icache_sync_wbus[535+:1];
    assign way0_sync_data   = icache_sync_wbus[0+:256];
    assign way1_sync_data   = icache_sync_wbus[279+:256];

    // ram control
    // tagv ram
    assign way0_tagv_cen    = (dcache_sync_rvalid || icache_sync_wvalid)
                            | (valid && next_state == STATE_LOOKUP)                                 // look up
                            | ((curr_state == STATE_MISS && next_state == STATE_REPLACE) & !replace_way) // replace
                            | (ret_valid && !mb_replace_way);        // refill
    assign way1_tagv_cen    = (dcache_sync_rvalid || icache_sync_wvalid)
                            | (valid && next_state == STATE_LOOKUP)                                 // look up
                            | ((curr_state == STATE_MISS && next_state == STATE_REPLACE) & replace_way) // replace
                            | (ret_valid && mb_replace_way);         // refill

    assign way0_tagv_wen    = icache_sync_wvalid
                            | (!uncache_r && (ret_valid && !mb_replace_way));         // refill
    assign way1_tagv_wen    = icache_sync_wvalid
                            | (!uncache_r && (ret_valid && mb_replace_way));         // refill

    assign tagv_din         = {(`ysyx_040991_CACHE_TAG_WD+1){ret_valid}} & {tag_r, 1'b1}; // refill

    assign way0_tagv_din    = icache_sync_wvalid ? way0_sync_tagv : tagv_din;
    assign way1_tagv_din    = icache_sync_wvalid ? way1_sync_tagv : tagv_din;
    
    assign tagv_addr        = ({`ysyx_040991_CACHE_INDEX_WD{dcache_sync_rvalid}} & dcache_sync_raddr)
                            | ({`ysyx_040991_CACHE_INDEX_WD{icache_sync_wvalid}} & icache_sync_waddr)
                            | ({`ysyx_040991_CACHE_INDEX_WD{valid && next_state == STATE_LOOKUP}} & index) // look up
                            | ({`ysyx_040991_CACHE_INDEX_WD{curr_state == STATE_MISS && next_state == STATE_REPLACE}} & index_r) // replace; read the way to be updated
                            | ({`ysyx_040991_CACHE_INDEX_WD{ret_valid}} & index_r);    // refill; update the way required update

    // d ram
    assign way0_d_cen   = (dcache_sync_rvalid || icache_sync_wvalid)
                        | (wb_valid && wb_way_id == 1'b0)           // hit write
                        | ((curr_state == STATE_MISS && next_state == STATE_REPLACE) & !replace_way) // replace
                        | (ret_valid && !mb_replace_way);            // replace way
    assign way1_d_cen   = (dcache_sync_rvalid || icache_sync_wvalid)
                        | (wb_valid && wb_way_id == 1'b1)           // hit write
                        | ((curr_state == STATE_MISS && next_state == STATE_REPLACE) & replace_way) // replace
                        | (ret_valid && mb_replace_way);             // replace way

    assign way0_d_wen   = icache_sync_wvalid
                        | (!uncache_r && ((wb_valid && wb_way_id == 1'b0)           // hit write
                        | ((curr_state == STATE_MISS && next_state == STATE_REPLACE) & !replace_way) // replace
                        | (ret_valid && !mb_replace_way)));
    assign way1_d_wen   = icache_sync_wvalid
                        | (!uncache_r && ((wb_valid && wb_way_id == 1'b1)           // hit write
                        | ((curr_state == STATE_MISS && next_state == STATE_REPLACE) & replace_way) // replace
                        | (ret_valid && mb_replace_way)));
    
    assign d_din        = (wb_valid & 1'b1)       // hit write
                        | ((ret_valid && op_r==`ysyx_040991_CACHE_OP_WRITE) & 1'b1)  // refill; store: dirty
                        | ((ret_valid && op_r==`ysyx_040991_CACHE_OP_READ) & 1'b0);  // refill; load: not dirty
    
    assign way0_d_din   =  icache_sync_wvalid ? way0_sync_d : d_din;
    assign way1_d_din   =  icache_sync_wvalid ? way1_sync_d : d_din;

    assign d_addr       = ({`ysyx_040991_CACHE_INDEX_WD{dcache_sync_rvalid}} & dcache_sync_raddr)
                        | ({`ysyx_040991_CACHE_INDEX_WD{icache_sync_wvalid}} & icache_sync_waddr)
                        | ({`ysyx_040991_CACHE_INDEX_WD{wb_valid}} & wb_index)  // 
                        | ({`ysyx_040991_CACHE_INDEX_WD{curr_state == STATE_MISS && next_state == STATE_REPLACE}} & index_r) // read the way to be updated
                        | ({`ysyx_040991_CACHE_INDEX_WD{ret_valid}} & index_r);     // refill: update the way required

    // data ram
    assign way0_data_cen    = (dcache_sync_rvalid || icache_sync_wvalid)
                            | (valid && next_state == STATE_LOOKUP)   // lookup
                            | (wb_valid && wb_way_id == 1'b0)   // hit write
                            | ((curr_state == STATE_MISS && next_state == STATE_REPLACE) & !replace_way) // replace
                            | (ret_valid && !mb_replace_way);   // refill
    assign way1_data_cen    = (dcache_sync_rvalid || icache_sync_wvalid)
                            | (valid && next_state == STATE_LOOKUP)   // lookup                       
                            | (wb_valid && wb_way_id == 1'b1)   // hit write
                            | ((curr_state == STATE_MISS && next_state == STATE_REPLACE) & replace_way) // replace
                            | (ret_valid && mb_replace_way);   // refill
    
    assign way0_data_wen    = icache_sync_wvalid
                            | (!uncache_r && ((wb_valid && wb_way_id == 1'b0)   // hit write
                            | (ret_valid && !mb_replace_way)));   // refill
    assign way1_data_wen    = icache_sync_wvalid
                            | (!uncache_r && ((wb_valid && wb_way_id == 1'b1)   // hit write
                            | (ret_valid && mb_replace_way)));    // refill
    wire [63:0] refill_datamask;
    assign refill_datamask = {{8{wstrb_r[7]}}, {8{wstrb_r[6]}}, {8{wstrb_r[5]}}, {8{wstrb_r[4]}}, {8{wstrb_r[3]}}, {8{wstrb_r[2]}}, {8{wstrb_r[1]}}, {8{wstrb_r[0]}}};  
    assign data_din         = ({256{wb_valid}} & {4{wb_wdata}})  // hit write 
                            | ({256{ret_valid && op_r == `ysyx_040991_CACHE_OP_READ}} & {4{ret_data}})                                      // refill: always refill ret data for load operations
                            | ({256{ret_valid && op_r == `ysyx_040991_CACHE_OP_WRITE && mb_rbeat_cnt == offset_r[4:3]}} & {4{(ret_data & ~refill_datamask) | (wdata_r & refill_datamask)}})     // refill: store wdata_r in wbank
                            | ({256{ret_valid && op_r == `ysyx_040991_CACHE_OP_WRITE && mb_rbeat_cnt != offset_r[4:3]}} & {4{ret_data}});   // refill: store ret data for the rest banks

    assign way0_data_din    = icache_sync_wvalid ? way0_sync_data : data_din;
    assign way1_data_din    = icache_sync_wvalid ? way1_sync_data : data_din;

    assign data_raddr       = ({`ysyx_040991_CACHE_INDEX_WD{dcache_sync_rvalid}} & dcache_sync_raddr)
                            | ({`ysyx_040991_CACHE_INDEX_WD{valid && next_state == STATE_LOOKUP && !hazard_case0}} & index) // lookup
                            | ({`ysyx_040991_CACHE_INDEX_WD{curr_state == STATE_MISS && next_state == STATE_REPLACE}} & index_r); // replace: read the way to be updated
                            
    assign data_waddr       = ({`ysyx_040991_CACHE_INDEX_WD{icache_sync_wvalid}} & icache_sync_waddr)
                            | ({`ysyx_040991_CACHE_INDEX_WD{wb_valid}} & wb_index)  // hit write: update the way that hit write
                            | ({`ysyx_040991_CACHE_INDEX_WD{ret_valid}} & index_r); // refill: update the way required update 

    assign data_wstrb       = ({8{icache_sync_wvalid}} & 8'hff)
                            | ({8{wb_valid}} & wb_bank_wstrb) 
                            | ({8{ret_valid}} & 8'hff);    // refill: always refill ret data for load operations

    assign data_rbank       = ({2{dcache_sync_rvalid}} & 2'b11)
                            | ({2{valid && next_state == STATE_LOOKUP && !hazard_case1}} &  (({2{offset[4] == 'b0}} & 2'b01)
                                                                                            |({2{offset[4] == 'b1}} & 2'b10))) // lookup
                            | ({2{curr_state == STATE_MISS && next_state == STATE_REPLACE}} & 2'b11); // replace


    assign data_wbank       = ({4{icache_sync_wvalid}} & 4'b1111)
                            | ({4{wb_valid}} &  (({4{wb_bank_id == 2'b00}} & 4'b0001)
                                                |({4{wb_bank_id == 2'b01}} & 4'b0010)
                                                |({4{wb_bank_id == 2'b10}} & 4'b0100)
                                                |({4{wb_bank_id == 2'b11}} & 4'b1000)))     // hit write
                            | ({4{ret_valid}} & (({4{mb_rbeat_cnt == 2'b00}} & 4'b0001)
                                                |({4{mb_rbeat_cnt == 2'b01}} & 4'b0010)
                                                |({4{mb_rbeat_cnt == 2'b10}} & 4'b0100)
                                                |({4{mb_rbeat_cnt == 2'b11}} & 4'b1000)));  // refill

    // outputs 
    reg [6:0] curr_state_d; 
    always @(posedge clk) begin
        if (rst) begin
            curr_state_d <= STATE_IDLE;
        end
        else begin
            curr_state_d <= curr_state;
        end
    end
    // to cpu core
    always @(*) begin
        if (curr_state == STATE_IDLE && !hazard_case1) begin
            addr_ok = 1'b1;
        end
        else if (curr_state == STATE_LOOKUP && next_state == STATE_LOOKUP && cache_hit && valid) begin
            addr_ok = 1'b1;
        end
        else begin
            addr_ok = 1'b0;
        end
    end

    always @(*) begin
        if (curr_state == STATE_LOOKUP && cache_hit) begin
            data_ok = 1'b1;
        end
        else if (curr_state == STATE_LOOKUP && wb_curr_state == WB_STATE_WRITE && cache_hit) begin
            data_ok = 1'b1;
        end
        else if (uncache_r && (op_r == `ysyx_040991_CACHE_OP_WRITE) && (curr_state_d == STATE_MISS) && (curr_state == STATE_REPLACE)) begin
            data_ok = 1'b1;
        end
        else if (curr_state == STATE_REFILL && ret_valid && (mb_rbeat_cnt == offset_r[4:3] || (uncache_r && op_r == `ysyx_040991_CACHE_OP_READ))) begin
            data_ok = 1'b1;
        end
        else begin
            data_ok = 1'b0;
        end
    end

    wire [63:0] wb_datamask;
    assign wb_datamask = {{8{wb_bank_wstrb[7]}}, {8{wb_bank_wstrb[6]}}, {8{wb_bank_wstrb[5]}}, {8{wb_bank_wstrb[4]}}, {8{wb_bank_wstrb[3]}}, {8{wb_bank_wstrb[2]}}, {8{wb_bank_wstrb[1]}}, {8{wb_bank_wstrb[0]}}};  

    always @(*) begin
        if (curr_state == STATE_LOOKUP && cache_hit && op_r == `ysyx_040991_CACHE_OP_READ && !hazard_case0) begin
            rdata = load_res;
        end
        else if (curr_state == STATE_LOOKUP && hazard_case0) begin // bypass data for hazard case 0
            rdata = (load_res & ~wb_datamask) | (wb_wdata & wb_datamask); 
        end
        else if (curr_state == STATE_REFILL && ret_valid && (mb_rbeat_cnt == offset_r[4:3])) begin
            rdata = ret_data;
        end
        else if (curr_state == STATE_REFILL && ret_valid && (uncache_r && op_r == `ysyx_040991_CACHE_OP_READ)) begin
            if (tag_r[20 : 1] == 20'h1000_0) begin // uart
                rdata = {8{ret_data[7:0]}};
            end
            else if (tag_r[20 : 5] == 16'h0200) begin // clint
                rdata = ret_data;
            end
            else begin
                rdata = {2{ret_data[31:0]}};
            end
        end
        else begin
            rdata = 64'b0;
        end
    end
      
    // to axi4 module
    // rd signals
    assign rd_req = (curr_state == STATE_REPLACE) && (uncache_r ? op_r == `ysyx_040991_CACHE_OP_READ : 1'b1); // arvalid
    always @(posedge clk) begin
        if (rst) begin
            rd_type <= 4'b0;
        end
        else if (next_state == STATE_REPLACE) begin
            if (uncache_r) begin
                if (tag_r[20 : 1] == 20'h1000_0) begin // uart
                    rd_type <= 4'b1000;
                end
                else if (tag_r[20 : 5] == 16'h0200) begin // clint
                    rd_type <= 4'b0001;
                end
                else begin
                    rd_type <= 4'b0010;     // read 32 bit mmio
                end
            end
            else begin
                rd_type <= 4'b0000;     // read cache line 
            end
        end
        else begin
            rd_type <= 4'b0;
        end
    end

    always @(posedge clk) begin
        if (rst) begin
            rd_addr <= 32'b0;
        end
        else if (next_state == STATE_REPLACE) begin
            if (uncache_r) begin
                if (tag_r[20 : 1] == 20'h1000_0) begin
                    rd_addr <= {tag_r, index_r, offset_r};
                end
                else if (tag_r[20 : 5] == 16'h0200) begin 
                    rd_addr <= {tag_r, index_r, offset_r[4:3], 3'b0};
                end
                else begin
                    rd_addr <= {tag_r, index_r, offset_r[4:2], 2'b0}; // masked addr for 64bit
                end
            end
            else begin
                rd_addr <= {tag_r, index_r, 5'b0};
            end
        end
        else begin
            rd_addr <= 32'b0;
        end
    end

    // wr signals

    wire wr_flag;
    assign wr_flag = (curr_state_d == STATE_MISS) && (curr_state == STATE_REPLACE);
    always @(posedge clk) begin
        if (rst) begin
            wr_req <= 1'b0;
        end
        else if (wr_flag) begin // only valid for one cycle
            if (uncache_r) begin
                wr_req <= op_r == `ysyx_040991_CACHE_OP_WRITE;
            end
            else begin
                wr_req <= is_dirty;
            end
        end
        else begin
            wr_req <= 1'b0;
        end
    end

    assign replace_data = mb_replace_way ? way1_data_dout : way0_data_dout;
    wire [`ysyx_040991_CACHE_TAG_WD-1:0] replace_tag;
    assign replace_tag = mb_replace_way ? way1_tag_dout: way0_tag_dout;
    always @(posedge clk) begin
        if (rst) begin
            wr_data <= 256'b0;
        end
        else if (wr_flag) begin
            if (uncache_r) begin
                wr_data <= {192'b0, wdata_r};
            end
            else begin
                wr_data <= replace_data;
            end
        end
        else begin
            wr_data <= 256'b0;
        end
    end

    always @(posedge clk) begin
        if (rst) begin
            wr_addr <= 32'b0;
        end
        else if (wr_flag) begin
            if (uncache_r) begin
                if (tag_r[20 : 1] == 20'h1000_0) begin // uart mmio
                    wr_addr <= {tag_r, index_r, offset_r}; // full addr
                end
                else if (tag_r[20 : 5] == 16'h0200) begin
                    wr_addr <= {tag_r, index_r, offset_r[4:3], 3'b0};
                end
                else begin
                    wr_addr <= {tag_r, index_r, offset_r[4:2], 2'b0};
                end
            end
            else begin
                wr_addr <= {replace_tag, index_r, 5'b0};
            end
        end
        else begin
            wr_addr <= 32'b0;
        end
    end

    always @(posedge clk) begin
        if (rst) begin
            wr_type <= 4'b0;
        end
        else if (wr_flag) begin
            if (uncache_r) begin
                if (tag_r[20 : 1] == 20'h1000_0) begin // uart mmio
                    wr_type <= 4'b1000;    // uart 8bit
                end
                else if (tag_r[20 : 5] == 16'h0200) begin
                    wr_type <= 4'b0001;
                end
                else begin
                    wr_type <= 4'b0010; // write 32bit data
                end
            end
            else begin
                wr_type <= 4'b0000; // write the entire cache line
            end
        end
        else begin
            wr_type <= 4'b0;
        end
    end
    
    always @(posedge clk) begin
        if (rst) begin
            wr_wstrb <= 8'b0;
        end
        else if (wr_flag) begin
            if (uncache_r) begin
                if (tag_r[20 : 1] == 20'h1000_0) begin // uart mmio
                    wr_wstrb <= 8'b01;    // uart 8bit
                end
                else if (tag_r[20 : 5] == 16'h0200) begin
                    wr_wstrb <= 8'hff;
                end
                else begin
                    if (offset_r[2] == 1'b0) begin
                        wr_wstrb <= 8'h0f;
                    end
                    else begin
                        wr_wstrb <= 8'hf0;
                    end
                end
            end
            else begin
                wr_wstrb <= 8'hff;
            end
        end
        else begin
            wr_wstrb <= 8'b0;
        end
    end



    // sync signals
    assign icache_sync_wready = curr_state == STATE_IDLE && next_state == STATE_IDLE;
    assign dcache_sync_rready = curr_state == STATE_IDLE && next_state == STATE_IDLE;
    assign dcache_sync_rbus = { way1_tag_dout, way1_v_dout, way1_d_dout, way1_data_dout, 
                                way0_tag_dout, way0_v_dout, way0_d_dout, way0_data_dout};
endmodule   

module ysyx_040991_cache_data_ram (
    input cen,
    input [`ysyx_040991_CACHE_INDEX_WD-1:0] raddr,
    input [`ysyx_040991_CACHE_INDEX_WD-1:0] waddr,
    input [`ysyx_040991_CACHE_LINE_WD-1:0] din,
    input wen,
    input [1:0] rbank,
    input [3:0] wbank,
    input [7:0] wmask,
    output [`ysyx_040991_CACHE_LINE_WD-1:0] dout,
    
    // data_bank0 ram0
    input  [128 -1:0]   bank0_Q,
    output              bank0_CEN,
    output              bank0_WEN,
    output [128 -1:0]   bank0_BWEN,
    output [6   -1:0]   bank0_A,
    output [128 -1:0]   bank0_D,
    // data_bank1 ram1
    input  [128 -1:0]   bank1_Q,
    output              bank1_CEN,
    output              bank1_WEN,
    output [128 -1:0]   bank1_BWEN,
    output [6   -1:0]   bank1_A,
    output [128 -1:0]   bank1_D

);  
    wire [63:0] unused [0:3]; 
    wire [63:0] wdata_mask; 
    assign wdata_mask = {{8{wmask[7]}}, {8{wmask[6]}}, {8{wmask[5]}}, {8{wmask[4]}}, {8{wmask[3]}}, {8{wmask[2]}}, {8{wmask[1]}}, {8{wmask[0]}}};

    
    assign bank0_CEN    =   ~(cen & (wbank[0] || wbank[1] || rbank[0]));
    assign bank0_WEN    =   ~(wen & (wbank[0] || wbank[1]));
    assign bank0_BWEN   =   ~{wbank[1] ? wdata_mask : 64'b0, wbank[0] ? wdata_mask : 64'b0};
    assign bank0_A      =   (rbank[0]) ? raddr : (wbank[0] || wbank[1]) ? waddr : 6'b0;
    assign bank0_D      =   din[127:0]; //{din[64 +: 64], din[0 +: 64]};

    assign bank1_CEN    =   ~(cen & (wbank[2] || wbank[3] || rbank[1]));
    assign bank1_WEN    =   ~(wen & (wbank[2] || wbank[3]));
    assign bank1_BWEN   =   ~{wbank[3] ? wdata_mask : 64'b0, wbank[2] ? wdata_mask : 64'b0};
    assign bank1_A      =   (rbank[1]) ? raddr : (wbank[2] || wbank[3]) ? waddr : 6'b0;
    assign bank1_D      =   din[255:128]; //{din[192 +: 64], din[128 +: 64]};

    assign dout = {bank1_Q, bank0_Q};

endmodule

module ysyx_040991_cache_d_ram (
    input clk,
    input rst,
    input cen,
    input [`ysyx_040991_CACHE_INDEX_WD-1:0] addr,
    input din,
    input wen,

    output reg dout
);
    integer i;
    reg d_ram [0:`ysyx_040991_CACHE_RAM_DEPTH-1];
    always @(posedge clk) begin
        if (rst) begin
            for (i = 0; i < 2**`ysyx_040991_CACHE_INDEX_WD; i=i+1) begin
                d_ram[i] <= 'b0;
            end
        end
        else if (wen && cen) begin
            d_ram[addr] <= din;
        end
    end
    always @(posedge clk) begin
        if (rst) begin
            dout <= 'b0;
        end
        else begin  
            dout <= cen ? d_ram[addr] : 'b0;
        end
    end
endmodule

module ysyx_040991_cache_sync (
    input clk,
    input rst,

    input es_fence,
    output reg es_fence_complete,

    input icache_sync_wready,
    output reg [`ysyx_040991_CACHE_INDEX_WD-1 : 0] icache_sync_waddr,
    output reg [`ysyx_040991_CACHE_SYNC_BUS_WD-1 : 0] icache_sync_wbus,
    output reg icache_sync_wvalid,
    
    input dcache_sync_rready,
    output reg dcache_sync_rvalid,
    output reg [`ysyx_040991_CACHE_INDEX_WD-1 : 0] dcache_sync_raddr,
    input [`ysyx_040991_CACHE_SYNC_BUS_WD-1 : 0] dcache_sync_rbus

    );

    reg [6:0] sync_counter;
    reg [1:0] curr_state, next_state;

    reg  [`ysyx_040991_CACHE_SYNC_BUS_WD-1 : 0] sync_bus_buffer;

    localparam [1:0] STATE_IDLE    =   2'b01;
    localparam [1:0] STATE_SYNC    =   2'b10;

    always @(posedge clk) begin
        if (rst) begin
            curr_state <= STATE_IDLE;
        end
        else begin
            curr_state <= next_state;
        end
    end


    always @(*) begin
        case (curr_state) 
            STATE_IDLE: begin
                if (es_fence && icache_sync_wready && dcache_sync_rready) begin
                    next_state = STATE_SYNC;
                end
                else begin
                    next_state = STATE_IDLE;
                end
            end
            STATE_SYNC: begin
                if (sync_counter == 7'd65) begin
                    next_state = STATE_IDLE;
                end
                else begin
                    next_state = STATE_SYNC;
                end
            end
            default: begin
                next_state = STATE_IDLE;
            end
        endcase
    end


    always @(posedge clk) begin
        if (rst) begin
            sync_counter <= 'b0;
        end
        else if (curr_state == STATE_SYNC && sync_counter != 7'd65) begin
            sync_counter <= sync_counter + 'b1;
        end
        else if (sync_counter == 7'd65) begin
            sync_counter <= 'b0;
        end
    end

    always @(posedge clk) begin
        if (rst) begin
            sync_bus_buffer <= 'b0;
        end
        else if (sync_counter >= 'd1 && sync_counter <= 'd64) begin
            sync_bus_buffer <= dcache_sync_rbus;
        end
        else begin
            sync_bus_buffer <= 'b0;
        end
    end

    always @(posedge clk) begin
        if (rst) begin
            dcache_sync_rvalid <= 'b0;
        end
        else if (next_state == STATE_SYNC && sync_counter < 'd63) begin
            dcache_sync_rvalid <= 1'b1;
        end
        else if (sync_counter == 'd63 && dcache_sync_rvalid) begin
            dcache_sync_rvalid <= 1'b0;
        end
        else begin
            dcache_sync_rvalid <= 1'b0;
        end
    end

    always @(posedge clk) begin
        if (rst) begin
            icache_sync_wvalid <= 'b0;
        end
        else if (curr_state == STATE_SYNC && sync_counter >= 'd1 && sync_counter <= 'd64) begin
            icache_sync_wvalid <= 1'b1;
        end
        else if (sync_counter == 'd65 && icache_sync_wvalid) begin
            icache_sync_wvalid <= 1'b0;
        end
        else begin
            icache_sync_wvalid <= 1'b0;
        end
    end

    assign es_fence_complete = (curr_state == STATE_SYNC) && (sync_counter == 7'd65);
    assign icache_sync_wbus = sync_bus_buffer;
    assign dcache_sync_raddr = {6{dcache_sync_rvalid}} & sync_counter[5:0];
    assign icache_sync_waddr = {6{icache_sync_wvalid}} & (sync_counter[5:0] - 'd2);

endmodule

module ysyx_040991_cache_top #(
    // Thread ID Width
    parameter integer C_M_AXI_ID_WIDTH	    = 4,
    // Width of Address Bus
    parameter integer C_M_AXI_ADDR_WIDTH	= 32,
    // Width of Data Bus
    parameter integer C_M_AXI_ysyx_040991_DATA_WIDTH	= 64,
    // Width of User Write Address Bus
    parameter integer C_M_AXI_AWUSER_WIDTH	= 1,
    // Width of User Read Address Bus
    parameter integer C_M_AXI_ARUSER_WIDTH	= 1,
    // Width of User Write Data Bus
    parameter integer C_M_AXI_WUSER_WIDTH	= 1,
    // Width of User Read Data Bus
    parameter integer C_M_AXI_RUSER_WIDTH	= 1,
    // Width of User Response Bus
    parameter integer C_M_AXI_BUSER_WIDTH	= 1
    )(
    input clk,
    input rst,
    // ports of icache
    input                               icache_valid    ,
    input                               icache_uncache  ,
    input                               icache_op       , // 0: read only
    input   [`ysyx_040991_CACHE_INDEX_WD    -1:0]   icache_index    , 
    input   [`ysyx_040991_CACHE_TAG_WD      -1:0]   icache_tag      ,
    input   [`ysyx_040991_CACHE_OFFSET_WD   -1:0]   icache_offset   ,

    output                              icache_addr_ok  ,
    output                              icache_data_ok  ,
    output                     [63:0]   icache_rdata    ,

    // ports of dcache
    input                               dcache_valid    ,
    input                               dcache_uncache  ,
    input                               dcache_op       , // 0: read, 1: write
    input   [`ysyx_040991_CACHE_INDEX_WD    -1:0]   dcache_index    , 
    input   [`ysyx_040991_CACHE_TAG_WD      -1:0]   dcache_tag      ,
    input   [`ysyx_040991_CACHE_OFFSET_WD   -1:0]   dcache_offset   ,
    input                      [ 7:0]   dcache_wstrb    ,
    input                      [63:0]   dcache_wdata    ,

    output                              dcache_addr_ok  ,
    output                              dcache_data_ok  ,
    output                     [63:0]   dcache_rdata    ,

    input                               es_fence,
    output                              es_fence_complete,
    // axi port 
    // input wire  M_AXI_ACLK,
    // input wire  M_AXI_ARESETN,
    output wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_AWID,
    output wire [C_M_AXI_ADDR_WIDTH-1 : 0] M_AXI_AWADDR,
    output wire [7 : 0] M_AXI_AWLEN,
    output wire [2 : 0] M_AXI_AWSIZE,
    output wire [1 : 0] M_AXI_AWBURST,
    output wire  M_AXI_AWLOCK,
    output wire [3 : 0] M_AXI_AWCACHE,
    output wire [2 : 0] M_AXI_AWPROT,
    output wire [3 : 0] M_AXI_AWQOS,
    output wire [3 : 0] M_AXI_AWREGION,
    output wire [C_M_AXI_AWUSER_WIDTH-1 : 0] M_AXI_AWUSER,
    output wire  M_AXI_AWVALID,
    input wire  M_AXI_AWREADY,

    output wire [C_M_AXI_ysyx_040991_DATA_WIDTH-1 : 0] M_AXI_WDATA,
    output wire [C_M_AXI_ysyx_040991_DATA_WIDTH/8-1 : 0] M_AXI_WSTRB,
    output wire  M_AXI_WLAST,
    output wire [C_M_AXI_WUSER_WIDTH-1 : 0] M_AXI_WUSER,
    output wire  M_AXI_WVALID,
    input wire  M_AXI_WREADY,
 
    input wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_BID,
    input wire [1 : 0] M_AXI_BRESP,
    input wire [C_M_AXI_BUSER_WIDTH-1 : 0] M_AXI_BUSER,
    input wire  M_AXI_BVALID,
    output wire  M_AXI_BREADY,

    output wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_ARID,
    output wire [C_M_AXI_ADDR_WIDTH-1 : 0] M_AXI_ARADDR,
    output wire [7 : 0] M_AXI_ARLEN,
    output wire [2 : 0] M_AXI_ARSIZE,
    output wire [1 : 0] M_AXI_ARBURST,
    output wire  M_AXI_ARLOCK,
    output wire [3 : 0] M_AXI_ARCACHE,
    output wire [2 : 0] M_AXI_ARPROT,
    output wire [3 : 0] M_AXI_ARQOS,
    output wire [3 : 0] M_AXI_ARREGION,
    output wire [C_M_AXI_ARUSER_WIDTH-1 : 0] M_AXI_ARUSER,
    output wire  M_AXI_ARVALID,
    input wire  M_AXI_ARREADY,

    input wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_RID,
    input wire [C_M_AXI_ysyx_040991_DATA_WIDTH-1 : 0] M_AXI_RDATA,
    input wire [1 : 0] M_AXI_RRESP,
    input wire  M_AXI_RLAST,
    input wire [C_M_AXI_RUSER_WIDTH-1 : 0] M_AXI_RUSER,
    input wire  M_AXI_RVALID,
    output wire  M_AXI_RREADY,

    // icache
    // data_bank0 of ram0
    input  [128 -1:0]   icache_ram0_bank0_Q,
    output              icache_ram0_bank0_CEN,
    output              icache_ram0_bank0_WEN,
    output [128 -1:0]   icache_ram0_bank0_BWEN,
    output [6   -1:0]   icache_ram0_bank0_A,
    output [128 -1:0]   icache_ram0_bank0_D,
    // data_bank1 of ram0
    input  [128 -1:0]   icache_ram0_bank1_Q,
    output              icache_ram0_bank1_CEN,
    output              icache_ram0_bank1_WEN,
    output [128 -1:0]   icache_ram0_bank1_BWEN,
    output [6   -1:0]   icache_ram0_bank1_A,
    output [128 -1:0]   icache_ram0_bank1_D,
    // data_bank0 of ram1
    input  [128 -1:0]   icache_ram1_bank0_Q,
    output              icache_ram1_bank0_CEN,
    output              icache_ram1_bank0_WEN,
    output [128 -1:0]   icache_ram1_bank0_BWEN,
    output [6   -1:0]   icache_ram1_bank0_A,
    output [128 -1:0]   icache_ram1_bank0_D,
    // data_bank1 of ram1
    input  [128 -1:0]   icache_ram1_bank1_Q,
    output              icache_ram1_bank1_CEN,
    output              icache_ram1_bank1_WEN,
    output [128 -1:0]   icache_ram1_bank1_BWEN,
    output [6   -1:0]   icache_ram1_bank1_A,
    output [128 -1:0]   icache_ram1_bank1_D,

    // dcache
    // data_bank0 of ram0
    input  [128 -1:0]   dcache_ram0_bank0_Q,
    output              dcache_ram0_bank0_CEN,
    output              dcache_ram0_bank0_WEN,
    output [128 -1:0]   dcache_ram0_bank0_BWEN,
    output [6   -1:0]   dcache_ram0_bank0_A,
    output [128 -1:0]   dcache_ram0_bank0_D,
    // data_bank1 of ram0
    input  [128 -1:0]   dcache_ram0_bank1_Q,
    output              dcache_ram0_bank1_CEN,
    output              dcache_ram0_bank1_WEN,
    output [128 -1:0]   dcache_ram0_bank1_BWEN,
    output [6   -1:0]   dcache_ram0_bank1_A,
    output [128 -1:0]   dcache_ram0_bank1_D,
    // data_bank0 of ram1
    input  [128 -1:0]   dcache_ram1_bank0_Q,
    output              dcache_ram1_bank0_CEN,
    output              dcache_ram1_bank0_WEN,
    output [128 -1:0]   dcache_ram1_bank0_BWEN,
    output [6   -1:0]   dcache_ram1_bank0_A,
    output [128 -1:0]   dcache_ram1_bank0_D,
    // data_bank1 of ram1
    input  [128 -1:0]   dcache_ram1_bank1_Q,
    output              dcache_ram1_bank1_CEN,
    output              dcache_ram1_bank1_WEN,
    output [128 -1:0]   dcache_ram1_bank1_BWEN,
    output [6   -1:0]   dcache_ram1_bank1_A,
    output [128 -1:0]   dcache_ram1_bank1_D
    );
    

    wire            icache_rd_req      ;
    wire   [  3:0]  icache_rd_type     ;
    wire   [ 31:0]  icache_rd_addr     ;
    wire            icache_rd_rdy      ;
    wire            icache_ret_valid   ;
    wire            icache_ret_last    ;
    wire   [ 63:0]  icache_ret_data    ;

    wire            dcache_rd_req      ;
    wire   [  3:0]  dcache_rd_type     ;
    wire   [ 31:0]  dcache_rd_addr     ;
    wire            dcache_rd_rdy      ;
    wire            dcache_ret_valid   ;
    wire            dcache_ret_last    ;
    wire   [ 63:0]  dcache_ret_data    ;

    wire            dcache_wr_req      ;
    wire   [  3:0]  dcache_wr_type     ;
    wire   [ 31:0]  dcache_wr_addr     ;
    wire   [  7:0]  dcache_wr_wstrb    ;
    wire   [255:0]  dcache_wr_data     ;
    wire            dcache_wr_rdy      ;
    wire                                icache_sync_wready;
    wire   [`ysyx_040991_CACHE_INDEX_WD-1 : 0]      icache_sync_waddr;
    wire   [`ysyx_040991_CACHE_SYNC_BUS_WD-1 : 0]   icache_sync_wbus;
    wire                                icache_sync_wvalid;
    
    wire                                dcache_sync_rready;
    wire                                dcache_sync_rvalid;
    wire  [`ysyx_040991_CACHE_INDEX_WD-1 : 0]       dcache_sync_raddr;
    wire  [`ysyx_040991_CACHE_SYNC_BUS_WD-1 : 0]    dcache_sync_rbus;


    // unused wires
    wire wr_req_unused  ;
    wire [3:0] wr_type_unused ;
    wire [31:0] wr_addr_unused ;
    wire [7:0] wr_wstrb_unused;
    wire [255:0] wr_data_unused ;
    wire dcache_sync_rready_unused;
    wire dcache_sync_raddr_unused;
    wire [`ysyx_040991_CACHE_SYNC_BUS_WD-1:0]dcache_sync_rbus_unused;
    wire icache_sync_wready_unused;
    ysyx_040991_cache_core icache_core(
    	.clk       (clk       ),
        .rst       (rst       ),
        .valid     (icache_valid     ),
        .uncache   (icache_uncache   ),
        .op        (icache_op        ),
        .index     (icache_index     ),
        .tag       (icache_tag       ),
        .offset    (icache_offset    ),
        .wstrb     ('b0              ), // not required for icahce
        .wdata     ('b0              ), // not required for icahce
        .addr_ok   (icache_addr_ok   ),
        .data_ok   (icache_data_ok   ),
        .rdata     (icache_rdata     ),
     
        .rd_req    (icache_rd_req    ),
        .rd_type   (icache_rd_type   ),
        .rd_addr   (icache_rd_addr   ),
        .rd_rdy    (icache_rd_rdy    ),
        .ret_valid (icache_ret_valid ),
        .ret_last  (icache_ret_last  ),
        .ret_data  (icache_ret_data  ),
        .wr_req    (wr_req_unused      ), // not required for icahce
        .wr_type   (wr_type_unused     ), // not required for icahce
        .wr_addr   (wr_addr_unused     ), // not required for icahce
        .wr_wstrb  (wr_wstrb_unused    ), // not required for icahce
        .wr_data   (wr_data_unused     ), // not required for icahce
        .wr_rdy    (1'b1             ),
        .icache_sync_wready (icache_sync_wready ),
        .icache_sync_waddr  (icache_sync_waddr  ),
        .icache_sync_wbus   (icache_sync_wbus   ),
        .icache_sync_wvalid (icache_sync_wvalid ),
        .dcache_sync_rready (dcache_sync_rready_unused ), // not required for icahce
        .dcache_sync_rvalid ('b0                       ), // not required for icahce
        .dcache_sync_raddr  ('b0                       ), // not required for icahce
        .dcache_sync_rbus   (dcache_sync_rbus_unused   ), // not required for icahce
        .ram0_bank0_Q       (icache_ram0_bank0_Q    ),
        .ram0_bank0_CEN     (icache_ram0_bank0_CEN  ),
        .ram0_bank0_WEN     (icache_ram0_bank0_WEN  ),
        .ram0_bank0_BWEN    (icache_ram0_bank0_BWEN ),
        .ram0_bank0_A       (icache_ram0_bank0_A    ),
        .ram0_bank0_D       (icache_ram0_bank0_D    ),
        .ram0_bank1_Q       (icache_ram0_bank1_Q    ),
        .ram0_bank1_CEN     (icache_ram0_bank1_CEN  ),
        .ram0_bank1_WEN     (icache_ram0_bank1_WEN  ),
        .ram0_bank1_BWEN    (icache_ram0_bank1_BWEN ),
        .ram0_bank1_A       (icache_ram0_bank1_A    ),
        .ram0_bank1_D       (icache_ram0_bank1_D    ),
        .ram1_bank0_Q       (icache_ram1_bank0_Q    ),
        .ram1_bank0_CEN     (icache_ram1_bank0_CEN  ),
        .ram1_bank0_WEN     (icache_ram1_bank0_WEN  ),
        .ram1_bank0_BWEN    (icache_ram1_bank0_BWEN ),
        .ram1_bank0_A       (icache_ram1_bank0_A    ),
        .ram1_bank0_D       (icache_ram1_bank0_D    ),
        .ram1_bank1_Q       (icache_ram1_bank1_Q    ),
        .ram1_bank1_CEN     (icache_ram1_bank1_CEN  ),
        .ram1_bank1_WEN     (icache_ram1_bank1_WEN  ),
        .ram1_bank1_BWEN    (icache_ram1_bank1_BWEN ),
        .ram1_bank1_A       (icache_ram1_bank1_A    ),
        .ram1_bank1_D       (icache_ram1_bank1_D    )
    );

    ysyx_040991_cache_core dcache_core(
    	.clk       (clk       ),
        .rst       (rst       ),
        .valid     (dcache_valid     ),
        .uncache   (dcache_uncache   ),
        .op        (dcache_op        ),
        .index     (dcache_index     ),
        .tag       (dcache_tag       ),
        .offset    (dcache_offset    ),
        .wstrb     (dcache_wstrb     ),
        .wdata     (dcache_wdata     ),
        .addr_ok   (dcache_addr_ok   ),
        .data_ok   (dcache_data_ok   ),
        .rdata     (dcache_rdata     ),

        .rd_req    (dcache_rd_req    ),
        .rd_type   (dcache_rd_type   ),
        .rd_addr   (dcache_rd_addr   ),
        .rd_rdy    (dcache_rd_rdy    ),
        .ret_valid (dcache_ret_valid ),
        .ret_last  (dcache_ret_last  ),
        .ret_data  (dcache_ret_data  ),
        .wr_req    (dcache_wr_req    ),
        .wr_type   (dcache_wr_type   ),
        .wr_addr   (dcache_wr_addr   ),
        .wr_wstrb  (dcache_wr_wstrb  ),
        .wr_data   (dcache_wr_data   ),
        .wr_rdy    (dcache_wr_rdy    ),
        .icache_sync_wready (icache_sync_wready_unused  ),
        .icache_sync_waddr  ('b0                ),
        .icache_sync_wbus   ('b0                ),
        .icache_sync_wvalid ('b0                ),
        .dcache_sync_rready (dcache_sync_rready ),
        .dcache_sync_rvalid (dcache_sync_rvalid ),
        .dcache_sync_raddr  (dcache_sync_raddr  ),
        .dcache_sync_rbus   (dcache_sync_rbus   ),
        .ram0_bank0_Q       (dcache_ram0_bank0_Q    ),
        .ram0_bank0_CEN     (dcache_ram0_bank0_CEN  ),
        .ram0_bank0_WEN     (dcache_ram0_bank0_WEN  ),
        .ram0_bank0_BWEN    (dcache_ram0_bank0_BWEN ),
        .ram0_bank0_A       (dcache_ram0_bank0_A    ),
        .ram0_bank0_D       (dcache_ram0_bank0_D    ),
        .ram0_bank1_Q       (dcache_ram0_bank1_Q    ),
        .ram0_bank1_CEN     (dcache_ram0_bank1_CEN  ),
        .ram0_bank1_WEN     (dcache_ram0_bank1_WEN  ),
        .ram0_bank1_BWEN    (dcache_ram0_bank1_BWEN ),
        .ram0_bank1_A       (dcache_ram0_bank1_A    ),
        .ram0_bank1_D       (dcache_ram0_bank1_D    ),
        .ram1_bank0_Q       (dcache_ram1_bank0_Q    ),
        .ram1_bank0_CEN     (dcache_ram1_bank0_CEN  ),
        .ram1_bank0_WEN     (dcache_ram1_bank0_WEN  ),
        .ram1_bank0_BWEN    (dcache_ram1_bank0_BWEN ),
        .ram1_bank0_A       (dcache_ram1_bank0_A    ),
        .ram1_bank0_D       (dcache_ram1_bank0_D    ),
        .ram1_bank1_Q       (dcache_ram1_bank1_Q    ),
        .ram1_bank1_CEN     (dcache_ram1_bank1_CEN  ),
        .ram1_bank1_WEN     (dcache_ram1_bank1_WEN  ),
        .ram1_bank1_BWEN    (dcache_ram1_bank1_BWEN ),
        .ram1_bank1_A       (dcache_ram1_bank1_A    ),
        .ram1_bank1_D       (dcache_ram1_bank1_D    )
    );

    ysyx_040991_axi_bridge 
    #(
        .C_M_AXI_ID_WIDTH           (C_M_AXI_ID_WIDTH           ),
        .C_M_AXI_ADDR_WIDTH         (C_M_AXI_ADDR_WIDTH         ),
        .C_M_AXI_ysyx_040991_DATA_WIDTH         (C_M_AXI_ysyx_040991_DATA_WIDTH         ),
        .C_M_AXI_AWUSER_WIDTH       (C_M_AXI_AWUSER_WIDTH       ),
        .C_M_AXI_ARUSER_WIDTH       (C_M_AXI_ARUSER_WIDTH       ),
        .C_M_AXI_WUSER_WIDTH        (C_M_AXI_WUSER_WIDTH        ),
        .C_M_AXI_RUSER_WIDTH        (C_M_AXI_RUSER_WIDTH        ),
        .C_M_AXI_BUSER_WIDTH        (C_M_AXI_BUSER_WIDTH        )
    )
    u_axi_bridge(
    	.M_AXI_ACLK       (clk              ),
        .M_AXI_ARESETN    (!rst             ),
        .icache_rd_req    (icache_rd_req    ),
        .icache_rd_type   (icache_rd_type   ),
        .icache_rd_addr   (icache_rd_addr   ),
        .icache_rd_rdy    (icache_rd_rdy    ),
        .icache_ret_valid (icache_ret_valid ),
        .icache_ret_last  (icache_ret_last  ),
        .icache_ret_data  (icache_ret_data  ),

        .dcache_rd_req    (dcache_rd_req    ),
        .dcache_rd_type   (dcache_rd_type   ),
        .dcache_rd_addr   (dcache_rd_addr   ),
        .dcache_rd_rdy    (dcache_rd_rdy    ),
        .dcache_ret_valid (dcache_ret_valid ),
        .dcache_ret_last  (dcache_ret_last  ),
        .dcache_ret_data  (dcache_ret_data  ),
        .dcache_wr_req    (dcache_wr_req    ),
        .dcache_wr_type   (dcache_wr_type   ),
        .dcache_wr_addr   (dcache_wr_addr   ),
        .dcache_wr_wstrb  (dcache_wr_wstrb  ),
        .dcache_wr_data   (dcache_wr_data   ),
        .dcache_wr_rdy    (dcache_wr_rdy    ),

        .M_AXI_AWID       (M_AXI_AWID       ),
        .M_AXI_AWADDR     (M_AXI_AWADDR     ),
        .M_AXI_AWLEN      (M_AXI_AWLEN      ),
        .M_AXI_AWSIZE     (M_AXI_AWSIZE     ),
        .M_AXI_AWBURST    (M_AXI_AWBURST    ),
        .M_AXI_AWLOCK     (M_AXI_AWLOCK     ),
        .M_AXI_AWCACHE    (M_AXI_AWCACHE    ),
        .M_AXI_AWPROT     (M_AXI_AWPROT     ),
        .M_AXI_AWQOS      (M_AXI_AWQOS      ),
        .M_AXI_AWREGION   (M_AXI_AWREGION   ),
        .M_AXI_AWUSER     (M_AXI_AWUSER     ),
        .M_AXI_AWVALID    (M_AXI_AWVALID    ),
        .M_AXI_AWREADY    (M_AXI_AWREADY    ),
        .M_AXI_WDATA      (M_AXI_WDATA      ),
        .M_AXI_WSTRB      (M_AXI_WSTRB      ),
        .M_AXI_WLAST      (M_AXI_WLAST      ),
        .M_AXI_WUSER      (M_AXI_WUSER      ),
        .M_AXI_WVALID     (M_AXI_WVALID     ),
        .M_AXI_WREADY     (M_AXI_WREADY     ),
        .M_AXI_BID        (M_AXI_BID        ),
        .M_AXI_BRESP      (M_AXI_BRESP      ),
        .M_AXI_BUSER      (M_AXI_BUSER      ),
        .M_AXI_BVALID     (M_AXI_BVALID     ),
        .M_AXI_BREADY     (M_AXI_BREADY     ),
        .M_AXI_ARID       (M_AXI_ARID       ),
        .M_AXI_ARADDR     (M_AXI_ARADDR     ),
        .M_AXI_ARLEN      (M_AXI_ARLEN      ),
        .M_AXI_ARSIZE     (M_AXI_ARSIZE     ),
        .M_AXI_ARBURST    (M_AXI_ARBURST    ),
        .M_AXI_ARLOCK     (M_AXI_ARLOCK     ),
        .M_AXI_ARCACHE    (M_AXI_ARCACHE    ),
        .M_AXI_ARPROT     (M_AXI_ARPROT     ),
        .M_AXI_ARQOS      (M_AXI_ARQOS      ),
        .M_AXI_ARREGION   (M_AXI_ARREGION   ),
        .M_AXI_ARUSER     (M_AXI_ARUSER     ),
        .M_AXI_ARVALID    (M_AXI_ARVALID    ),
        .M_AXI_ARREADY    (M_AXI_ARREADY    ),
        .M_AXI_RID        (M_AXI_RID        ),
        .M_AXI_RDATA      (M_AXI_RDATA      ),
        .M_AXI_RRESP      (M_AXI_RRESP      ),
        .M_AXI_RLAST      (M_AXI_RLAST      ),
        .M_AXI_RUSER      (M_AXI_RUSER      ),
        .M_AXI_RVALID     (M_AXI_RVALID     ),
        .M_AXI_RREADY     (M_AXI_RREADY     )
    );
    
    ysyx_040991_cache_sync u_cache_sync(
    	.clk                (clk                ),
        .rst                (rst                ),
        .es_fence           (es_fence           ),
        .es_fence_complete  (es_fence_complete  ),
        .icache_sync_wready (icache_sync_wready ),
        .icache_sync_waddr  (icache_sync_waddr  ),
        .icache_sync_wbus   (icache_sync_wbus   ),
        .icache_sync_wvalid (icache_sync_wvalid ),
        .dcache_sync_rready (dcache_sync_rready ),
        .dcache_sync_rvalid (dcache_sync_rvalid ),
        .dcache_sync_raddr  (dcache_sync_raddr  ),
        .dcache_sync_rbus   (dcache_sync_rbus   )
    );
    
endmodule

module ysyx_040991_csr_forwarding_unit(
    // ms to es
    input ms_csr_wen,
    input [11:0] ms_csr_waddr,

    // ws to es
    input ws_csr_wen,
    input [11:0] ws_csr_waddr,

    // id signals
    input [11:0] es_csr_raddr,
    output [2:0] es_csr_rdata_sel
);


    wire ms_csr_bypass;
    wire ws_csr_bypass;
    assign ms_csr_bypass = ms_csr_wen && (ms_csr_waddr == es_csr_raddr);
    assign ws_csr_bypass = ws_csr_wen && (ws_csr_waddr == es_csr_raddr);

    // [0] csr_rdata 
    assign es_csr_rdata_sel[0]  =   ~ms_csr_bypass &&  ~ws_csr_bypass; 
    // [1] ms result
    assign es_csr_rdata_sel[1]  =   ms_csr_bypass; 
    // [2] ws result
    assign es_csr_rdata_sel[2]  =   ~ms_csr_bypass &&  ws_csr_bypass;                             

endmodule

module ysyx_040991_csr(
    input clk,
    input rst,
    // external interrupt
    input timer_intr,

    // from es
    input   [11:0]  csr_raddr,
    output  [63:0]  csr_rdata,
    // from ws
    input           csr_wen,
    input   [11:0]  csr_waddr,
    input   [63:0]  csr_wdata,
    input   [ 5:0]  ws_exception,
    input           ws_exception_valid,
    input   [63:0]  ws_exception_pc,

    output  [63:0]  csr_branch_pc,

    // masked interrupt output
    output timer_interrupt_en
    );

    // Machine Trap Setup
    reg [63:0] csr_mstatus;
    reg [63:0] csr_mie;
    reg [63:0] csr_mtvec;

    // Machine Trap Handling
    reg [63:0] csr_mscratch;
    reg [63:0] csr_mepc;
    reg [63:0] csr_mcause;
    reg [63:0] csr_mip;
    wire ysyx_040991_MIP_MTIP;
    reg [63:0] csr_mhartid;
    // Machine Counter/Timers
    reg [ 1:0] csr_mpriv;
    reg [63:0] csr_mcycle;

    wire [1:0]  irq_priv;
    assign timer_interrupt_en = csr_mie[`ysyx_040991_MIP_MTIP] && csr_mstatus[`ysyx_040991_MSTATUS_MIE] && ysyx_040991_MIP_MTIP;
    assign irq_priv = `ysyx_040991_PRIV_MACHINE;

    // end
    // csr read port
    assign csr_rdata =    ({64{csr_raddr[11:0] == `ysyx_040991_CSR_MSTATUS_ADDR }} & csr_mstatus    )
                        | ({64{csr_raddr[11:0] == `ysyx_040991_CSR_MTVEC_ADDR   }} & csr_mtvec      )
                        | ({64{csr_raddr[11:0] == `ysyx_040991_CSR_MSCRATCH_ADDR}} & csr_mscratch   )
                        | ({64{csr_raddr[11:0] == `ysyx_040991_CSR_MIE_ADDR     }} & csr_mie        )
                        | ({64{csr_raddr[11:0] == `ysyx_040991_CSR_MIP_ADDR     }} & csr_mip        )
                        | ({64{csr_raddr[11:0] == `ysyx_040991_CSR_MEPC_ADDR    }} & csr_mepc       )
                        | ({64{csr_raddr[11:0] == `ysyx_040991_CSR_MCAUSE_ADDR  }} & csr_mcause     )
                        | ({64{csr_raddr[11:0] == `ysyx_040991_CSR_MCYCLE_ADDR  }} & csr_mcycle     )
                        | ({64{csr_raddr[11:0] == `ysyx_040991_CSR_MCYCLE_ADDR  }} & csr_mhartid    );

    wire is_interrupt;
    wire is_exception;
    wire is_mret;
    // exception[5:0] [5:4] exception, interrupt, ret [3:0] type
    assign is_mret      = (ws_exception[5:4] == `ysyx_040991_EXCEPTION_ERET) && ws_exception_valid;
    assign is_interrupt = (ws_exception[5:4] == `ysyx_040991_EXCEPTION_INTERRUPT) && ws_exception_valid && timer_interrupt_en;
    assign is_exception = (ws_exception[5:4] == `ysyx_040991_EXCEPTION_EXCEPTION) && ws_exception_valid;

    wire [3:0] interrupt_type;
    wire [3:0] exception_type;

    assign interrupt_type = ws_exception[3:0];
    assign exception_type = ws_exception[3:0];

    // csr write port
    always @(posedge clk) begin
        if (rst) begin
            csr_mpriv <= `ysyx_040991_PRIV_MACHINE;
        end
        else begin
            csr_mpriv <= `ysyx_040991_PRIV_MACHINE;
        end
    end
    always @(posedge clk) begin
        if (rst) begin
            csr_mepc <= 64'b0;
        end
        else if (is_interrupt && irq_priv == `ysyx_040991_PRIV_MACHINE) begin
            csr_mepc <= ws_exception_pc;
        end
        else if (is_exception) begin
            csr_mepc <= ws_exception_pc;
        end
        else if (csr_wen && csr_waddr == `ysyx_040991_CSR_MEPC_ADDR) begin
            csr_mepc <= csr_wdata;
        end
    end

    always @(posedge clk) begin
        if (rst) begin
            csr_mcause  <= 64'b0;
        end
        else if (is_interrupt && irq_priv == `ysyx_040991_PRIV_MACHINE) begin
            csr_mcause  <=  ({64{interrupt_type == `ysyx_040991_IRQ_M_SOFT    }} & {1'b1, 59'b0, `ysyx_040991_IRQ_M_SOFT })
                          | ({64{interrupt_type == `ysyx_040991_IRQ_M_TIMER   }} & {1'b1, 59'b0, `ysyx_040991_IRQ_M_TIMER})
                          | ({64{interrupt_type == `ysyx_040991_IRQ_M_EXT     }} & {1'b1, 59'b0, `ysyx_040991_IRQ_M_EXT  });
        end
        else if (is_exception) begin
            csr_mcause <= {60'b0, exception_type};
        end
        else if (csr_wen && csr_waddr == `ysyx_040991_CSR_MCAUSE_ADDR) begin
            csr_mcause <= {csr_wdata[63], 59'b0, csr_wdata[3:0]};
        end
    end

    always @(posedge clk) begin
        if (rst) begin
            csr_mstatus <= 64'h1800;
        end
        else if (is_interrupt && irq_priv == `ysyx_040991_PRIV_MACHINE) begin
            csr_mstatus[`ysyx_040991_MSTATUS_MIE]   <= 1'b0;
            csr_mstatus[`ysyx_040991_MSTATUS_MPIE]  <= csr_mstatus[`ysyx_040991_MSTATUS_MIE];
            csr_mstatus[`ysyx_040991_MSTATUS_MPP]   <= irq_priv;
        end
        else if (is_mret) begin
            csr_mstatus[`ysyx_040991_MSTATUS_MIE]   <= csr_mstatus[`ysyx_040991_MSTATUS_MPIE];
            csr_mstatus[`ysyx_040991_MSTATUS_MPIE]  <= 1'b1;
            csr_mstatus[`ysyx_040991_MSTATUS_MPP]   <= irq_priv;
        end
        else if (is_exception) begin
            csr_mstatus[`ysyx_040991_MSTATUS_MIE]   <= 1'b0;
            csr_mstatus[`ysyx_040991_MSTATUS_MPIE]  <= csr_mstatus[`ysyx_040991_MSTATUS_MIE];
            csr_mstatus[`ysyx_040991_MSTATUS_MPP]   <= csr_mpriv;
        end
        else if (csr_wen && csr_waddr == `ysyx_040991_CSR_MSTATUS_ADDR) begin
            csr_mstatus <= {(csr_wdata[14:13] == 2'b11) | (csr_wdata[16:15] == 2'b11), csr_wdata[62:0]};
        end
    end

    
    always @(posedge clk) begin
        if (rst) begin
            csr_mtvec <= 64'b0;
        end
        else if(csr_waddr == `ysyx_040991_CSR_MTVEC_ADDR && csr_wen) begin
            csr_mtvec <= csr_wdata;
        end
    end

    always @(posedge clk) begin
        if (rst) begin
            csr_mie <= 64'b0;
        end
        else if(csr_waddr == `ysyx_040991_CSR_MIE_ADDR && csr_wen) begin
            csr_mie <= csr_wdata;
        end
    end

    always @(posedge clk) begin
        if (rst) begin
            csr_mscratch <= 64'b0;
        end
        else if(csr_waddr == `ysyx_040991_CSR_MSCRATCH_ADDR && csr_wen) begin
            csr_mscratch <= csr_wdata;
        end
    end

    always @(posedge clk) begin
        if (rst) begin
            csr_mhartid <= 64'b0;
        end
        else if(csr_waddr == `ysyx_040991_CSR_MHARTID_ADDR && csr_wen) begin
            csr_mhartid <= csr_wdata;
        end
    end


    assign ysyx_040991_MIP_MTIP = timer_intr;
    assign csr_mip = {56'b0, ysyx_040991_MIP_MTIP, 7'b0};


    always @(posedge clk) begin
        if (rst) begin
            csr_mcycle <= 64'b0;
        end
        else begin
            csr_mcycle <= csr_mcycle + 1;
        end
    end

    // csr branch
    assign csr_branch_pc    =   ({64{is_interrupt && irq_priv == `ysyx_040991_PRIV_MACHINE}} & csr_mtvec)
                            |   ({64{is_mret}} & csr_mepc)
                            |   ({64{is_exception}} & csr_mtvec);

endmodule

module ysyx_040991_ctrl(
    // input clk,
    // input rst,
    // branch and jal signals from id stage
    input [`ysyx_040991_BR_BUS_WD  :0] ds_branch_bus,
    // jalr signals from exe stage
    input [`ysyx_040991_BR_BUS_WD-1:0] es_branch_bus,
    // to es stage
    output ds_to_es_hold,
    
    // to ms stage
    // output es_to_ms_hold,

    // to fs stage
    output [`ysyx_040991_BR_BUS_WD:0] br_bus

);

    // jalr > branch, jal
    wire br_taken;
    wire [`ysyx_040991_VADDR_WIDTH-1:0] br_target;
    wire br_stall;
    assign br_bus = {br_stall, br_taken, br_target};

    wire br_j_en;
    wire [`ysyx_040991_VADDR_WIDTH-1:0] br_j_pc;
    assign {br_stall, br_j_en, br_j_pc} = ds_branch_bus;

    wire jalr_en;
    wire [`ysyx_040991_VADDR_WIDTH-1:0] jalr_pc;
    assign {jalr_en, jalr_pc} = es_branch_bus;

      
    assign br_target =  jalr_en ?   jalr_pc :
                        br_j_en ?   br_j_pc : 
                                    64'b0   ;
    assign br_taken         = br_j_en || jalr_en;
    assign ds_to_es_hold    = jalr_en;
endmodule

module ysyx_040991_decoder_3_8(
    input [2:0] in,
    output [7:0] out
);
    genvar i;
    generate for (i=0; i < 8; i=i+1) begin: gen_for_dec_3_8
        assign out[i] = (in == i);
    end
    endgenerate
endmodule

module ysyx_040991_decoder_7_128(
    input  [ 6:0] in,
    output [127:0] out
);

    genvar i;
    generate for (i=0; i<128; i=i+1) begin : gen_for_dec_7_128
        assign out[i] = (in == i);
    end endgenerate

endmodule


module ysyx_040991_divider_cell(
    input                     clk,
    input                     rst,
    input                     en,

    input [64:0]            A, // dividend
    input [63:0]            B, // divisor
    input [63:0]            S_ci, // 
    input [62:0]            A_ci,
    input [8:0]             div_info_ci,
    output reg [8:0]        div_info_kp,
    output reg [62:0]       A_kp,
    output reg [63:0]       B_kp,
    output reg              rdy,
    output reg [63:0]       S,
    output reg [63:0]       R
);
    reg tmp;
    always @(posedge clk) begin
        if (rst) begin
            rdy     <= 'b0;
            S       <= 'b0;
            R       <= 'b0;
            B_kp    <= 'b0;
            A_kp    <= 'b0;
            div_info_kp <= 'b0;
        end
        else if (en) begin
            rdy     <= 1'b1;
            B_kp    <= B;  
            A_kp    <= A_ci;
            div_info_kp <= div_info_ci;
            if (A >= {1'b0, B}) begin
                S   <= (S_ci<<1) + 1'b1; 
         {tmp, R}   <= A - {1'b0, B};
            end
            else begin
                S   <= S_ci<<1;
         {tmp, R}   <= A; 
            end
        end
        else begin
            rdy     <= 'b0 ;
            S       <= 'b0 ;
            R       <= 'b0 ;
            B_kp    <= 'b0 ;
            A_kp    <= 'b0 ;
            div_info_kp <= 'b0;
        end
    end

endmodule


module ysyx_040991_divider #(
    parameter ysyx_040991_DATA_WIDTH = 64
)(
    input clk,
    input rst,
    input [ysyx_040991_DATA_WIDTH-1:0] div_src1,
    input [ysyx_040991_DATA_WIDTH-1:0] div_src2,
    input [3:0] div_op,
    input word_op, // word instructions 
    input div, // start div operation
    // output reg complete, // after 66 iterations 0-65
    output reg [ysyx_040991_DATA_WIDTH-1:0]  div_wb_result
);

    wire div_signed = div_op[3] | div_op[1]; // signed operands
    
    // step1 fetch abs of dividend & divisor and signed info
    wire A_sign, B_sign;
    assign A_sign = div_src1[ysyx_040991_DATA_WIDTH-1];
    assign B_sign = div_src2[ysyx_040991_DATA_WIDTH-1];
    reg [63:0] A_init; // dividend init value
    reg [63:0] B_init; // divisor init value
    reg data_rdy;
    reg [8:0] div_info;
    wire [63:0] A_abs; // dividend abs
    wire [63:0] B_abs; // divisor abs
    wire [63:0] div_src1_word;
    wire [63:0] div_src2_word;
    assign div_src1_word = word_op ? {div_signed ? {32{div_src1[31]}} : 32'b0, div_src1[31:0]} : div_src1;
    assign div_src2_word = word_op ? {div_signed ? {32{div_src2[31]}} : 32'b0, div_src2[31:0]} : div_src2;
    assign A_abs = A_sign ? (~div_src1_word+1) : div_src1_word;
    assign B_abs = B_sign ? (~div_src2_word+1) : div_src2_word;
    wire B_zero;
    assign B_zero = div_src2 == 'b0;
    always @(posedge clk) begin
        if (rst) begin
            A_init <= 64'b0;
            B_init <= 64'b0;
            div_info <= 9'b0;
            data_rdy <= 1'b0;
        end
        else if (div) begin
            A_init <= div_signed ? (A_abs) : div_src1_word;
            B_init <= div_signed ? (B_abs) : div_src2_word;
            div_info <= {B_zero, A_sign, B_sign, div_signed, div_op, word_op};
            data_rdy <= 1'b1;
        end
        else begin
            A_init <= 64'b0;
            B_init <= 64'b0;
            div_info <= 9'b0;
            data_rdy <= 1'b0;
        end
    end
    
    // step2 64stage pipeline
    wire [62:0]     A_bus [0:63];
    wire [63:0]     B_bus [0:63];
    wire [63:0]     R_bus [0:63];
    wire [63:0]     rdy_bus;
    wire [63:0]     S_bus [0:63];
    wire [8:0]      div_info_bus [0:63];
    // pipeline 0
    ysyx_040991_divider_cell u_divider_step0 ( 
        .clk      (clk),
        .rst      (rst),
        .en       (data_rdy),
        .A              ({64'b0, A_init[63]}),
        .B              (B_init),                  
        .S_ci           (64'b0),            // value of first S is 0
        .A_ci           (A_init[62:0]),     // store A init value
        .div_info_ci    (div_info),         // record div info
        //output
        .div_info_kp    (div_info_bus[63]),
        .A_kp           (A_bus[63]),        // pass value to next stage
        .B_kp           (B_bus[63]),        
        .rdy            (rdy_bus[63]),
        .S              (S_bus[63]),        // first s
        .R              (R_bus[63])         // risr
      );

    genvar i;
    generate
        for(i=1; i<=63; i=i+1) begin: sqrt_stepx
            ysyx_040991_divider_cell u_divider_step (
                .clk            (clk),
                .rst            (rst),
                .en             (rdy_bus[63-i+1]),
                .A              ({R_bus[63-i+1], A_bus[63-i+1][63-i]}),   // concact
                .B              (B_bus[63-i+1]),
                .S_ci           (S_bus[63-i+1]),
                .A_ci           (A_bus[63-i+1]),
                .div_info_ci    (div_info_bus[63-i+1]),
                //output
                .div_info_kp    (div_info_bus[63-i]),
                .B_kp           (B_bus[63-i]),
                .A_kp           (A_bus[63-i]),
                .rdy            (rdy_bus[63-i]),
                .S              (S_bus[63-i]),
                .R              (R_bus[63-i])
            );
        end // block: sqrt_stepx
    endgenerate
    
    // step_3 
    wire [63:0] S_final_abs, S_final;
    wire [63:0] R_final_abs, R_final;
    assign S_final_abs = S_bus[0];
    assign R_final_abs = R_bus[0];
    // div info final
    wire A_sign_final, B_sign_final, S_sign_final, R_sign_final;
    wire [3:0] div_op_final;
    wire word_op_final;
    wire div_signed_final;
    wire B_zero_final;
    assign {
        B_zero_final,
        A_sign_final,
        B_sign_final,
        div_signed_final,
        div_op_final,
        word_op_final
    } = div_info_bus[0];
    assign S_sign_final = A_sign_final ^ B_sign_final;
    assign R_sign_final = A_sign_final;
    
    assign S_final = S_sign_final ? (~S_final_abs+1) : S_final_abs;
    assign R_final = R_sign_final ? (~R_final_abs+1) : R_final_abs;
    wire [63:0] div_result;
    wire [63:0] rem_result;
    wire [63:0] divw_result;
    wire [63:0] remw_result;
    assign div_result = {64{rdy_bus[0]}} & {B_zero_final ?  S_final_abs : div_signed_final ? (S_final) : (S_final_abs)};
    assign rem_result = {64{rdy_bus[0]}} & (div_signed_final ? (R_final) : (R_final_abs));                                    

    assign divw_result = {64{rdy_bus[0]}} & {B_zero_final ? {{32{S_final_abs[31]}}, S_final_abs[31:0]} : {{32{div_result[31]}}, div_result[31:0]}};
    assign remw_result = {64{rdy_bus[0]}} & {{32{rem_result[31]}}, rem_result[31:0]};

    wire op_div     = div_op_final[ 3] & ~word_op_final;
    wire op_divu    = div_op_final[ 2] & ~word_op_final;
    wire op_rem     = div_op_final[ 1] & ~word_op_final;
    wire op_remu    = div_op_final[ 0] & ~word_op_final;

    wire op_divw    = div_op_final[ 3] & word_op_final;
    wire op_divuw   = div_op_final[ 2] & word_op_final;
    wire op_remw    = div_op_final[ 1] & word_op_final;
    wire op_remuw   = div_op_final[ 0] & word_op_final;
    
    // reg rdy_d;
    // always @(posedge clk) begin
    //     rdy_d <= rdy_bus[0];
    // end
    // divider result to mem stage directly
    always @ (posedge clk) begin
        // complete <= rdy_bus[0] & ~rdy_d; // complete signal only last one clock cycle
        div_wb_result  <= ({64{op_div   |op_divu    }}  & div_result)
                        | ({64{op_divw  |op_divuw   }}  & divw_result)
                        | ({64{op_rem   |op_remu    }}  & rem_result)
                        | ({64{op_remw  |op_remuw   }}  & remw_result);
    end
endmodule

module ysyx_040991_exe_stage(
    input                          clk           ,
    input                          rst         ,
    //allowin
    input                          ms_allowin    ,
    output                         es_allowin    ,
    //from ds
    input                          ds_to_es_valid,
    input  [`ysyx_040991_DS_TO_ES_BUS_WD -1:0] ds_to_es_bus  ,
    input  [`ysyx_040991_EXCEPTION_WD-1:0] ds_to_es_exception,
    //to ms
    output                         es_to_ms_valid,
    output [`ysyx_040991_ES_TO_MS_BUS_WD -1:0] es_to_ms_bus  ,
    output [`ysyx_040991_EXCEPTION_WD -1:0] es_to_ms_exception,
    //to ctrl
    input                          ds_to_es_hold,
    output [`ysyx_040991_BR_BUS_WD       -1:0] es_branch_bus,
    input ws_flush,
    input ms_is_exception,
    // dcache ports
    output                               dcache_valid    ,
    output                               dcache_uncache  ,
    output                               dcache_op       , // 0: read, 1: write
    output   [`ysyx_040991_CACHE_INDEX_WD    -1:0]   dcache_index    , 
    output   [`ysyx_040991_CACHE_TAG_WD      -1:0]   dcache_tag      ,
    output   [`ysyx_040991_CACHE_OFFSET_WD   -1:0]   dcache_offset   ,
    output                      [ 7:0]   dcache_wstrb    ,
    output                      [63:0]   dcache_wdata    ,

    input                                dcache_addr_ok  ,

    output                               es_fence,
    input                                es_fence_complete,
    // forwarding bus
    output  [`ysyx_040991_ES_FORWARDING_BUS_WD -1:0] es_forwarding_bus,
    // to multiplier divider
    output [`ysyx_040991_DATA_WIDTH-1:0] es_alu_src1,
    output [`ysyx_040991_DATA_WIDTH-1:0] es_alu_src2,
    output [ 3:0] es_mul_op,
    output [ 3:0] es_div_op,
    output es_word_op,
    output div,

    // read csr file
    output [11:0] es_csr_raddr,
    input  [63:0] es_csr_rdata,
    // forwarding csr register
    input [`ysyx_040991_CSR_FORWARDING_BUS_WD -1:0] ms_csr_forwarding_bus,
    input [`ysyx_040991_CSR_FORWARDING_BUS_WD -1:0] ws_csr_forwarding_bus

);

    reg         es_valid      ;
    wire        es_ready_go   ;
    reg  [`ysyx_040991_DS_TO_ES_BUS_WD-1:0] ds_to_es_bus_r;
    reg  [`ysyx_040991_EXCEPTION_WD-1:0] ds_to_es_exception_r;
    wire [10:0] es_alu_op       ;
    wire [ 4:0] es_src1_sel     ;
    wire [ 4:0] es_src2_sel     ;
    wire [ 6:0] es_load_op      ;
    wire [ 3:0] es_save_op      ;
    wire [ 5:0] es_wb_sel       ;
    wire es_rd_wen              ;
    wire es_data_ram_en         ;
    wire es_data_ram_wen        ;
    wire [ 4:0] es_rd_addr;
    wire [`ysyx_040991_DATA_WIDTH-1:0] es_rs1_data   ;
    wire [`ysyx_040991_DATA_WIDTH-1:0] es_rs2_data   ;
    wire [`ysyx_040991_VADDR_WIDTH-1:0] es_pc ;
    wire [31:0] es_inst;
    wire es_csr_wen;
    wire es_ds_stall;
    wire es_jalr_en;
    wire es_fence_i;
    wire div_complete;
    wire [63:0] es_csr_rdata_final;
    assign {
        es_fence_i      ,
        es_alu_op       ,  
        es_src1_sel     ,
        es_src2_sel     ,
        es_load_op      ,
        es_save_op      ,
        es_mul_op       ,
        es_div_op       ,
        es_word_op      ,
        es_wb_sel       ,
        es_rd_wen       ,
        es_data_ram_en  ,
        es_data_ram_wen ,
        es_rs1_data     ,
        es_rs2_data     ,
        es_rd_addr      ,
        es_jalr_en      ,
        es_csr_wen      ,
        es_ds_stall     ,
        es_pc           ,
        es_inst
    } = ds_to_es_bus_r;

    wire [`ysyx_040991_DATA_WIDTH-1:0] es_alu_result ;
    assign es_to_ms_bus = {
        // dcache_uncache      ,
        es_data_ram_en      ,
        es_wb_sel           ,
        es_load_op          ,  
        es_rd_wen           ,  
        es_rd_addr          ,  
        es_alu_result       ,
        es_csr_rdata_final  , 
        es_csr_wen          ,
        es_pc               ,
        es_inst             
    };

    wire [5:0] es_exception_curr;
    assign es_exception_curr = ds_to_es_exception_r;
    wire es_is_exception;
    assign es_is_exception = |es_exception_curr[5:4];

    assign es_forwarding_bus = {
        es_ds_stall && es_valid     ,
        es_rd_wen   && es_valid     ,
        es_rd_addr                  ,
        es_alu_result
    };

    reg ws_flush_r;
    always @(posedge clk) begin
        if (rst) begin
            ws_flush_r <= 1'b0;
        end else if (ws_flush) begin
            ws_flush_r <= 1'b1;
        end else if (ds_to_es_valid && es_allowin) begin
            ws_flush_r <= 1'b0;
        end
    end

    assign es_ready_go    = es_fence        ? (es_fence_complete || es_is_exception || ms_is_exception || ws_flush || ws_flush_r) : 
                            div             ? (div_complete || es_is_exception || ms_is_exception || ws_flush || ws_flush_r) : 
                            es_data_ram_en  ? ((dcache_valid && dcache_addr_ok) || es_is_exception || ms_is_exception || ws_flush || ws_flush_r) 
                                            : 1'b1; // div div_complete signal generated from exe stage
    
    assign es_allowin     = !es_valid || es_ready_go && ms_allowin;
    assign es_to_ms_valid =  es_valid && es_ready_go && !(ws_flush || ws_flush_r);
    always @(posedge clk) begin
        if (rst) begin
            es_valid <= 1'b0;
        end
        else if (es_allowin) begin
            es_valid <= ds_to_es_valid && !ds_to_es_hold;
        end
    
        if (ds_to_es_valid && es_allowin) begin
            ds_to_es_bus_r <= ds_to_es_bus;
            ds_to_es_exception_r <= ds_to_es_exception;
        end
    end

    // div signal generate
    reg [7:0] div_cnt;
    assign div = |es_div_op && es_valid || (div_cnt > 0);
    assign div_complete = div_cnt == 8'd66;
    always @(posedge clk) begin
        if (rst) begin
            div_cnt <= 8'b0;
        end
        else if (div_cnt == 8'd66) begin
            div_cnt <= 8'b0;
        end
        else if (div) begin
            div_cnt <= div_cnt + 1;
        end
        else begin
            div_cnt <= 8'b0;
        end
    end

    wire [`ysyx_040991_DATA_WIDTH-1:0] u_imm;
    wire [`ysyx_040991_DATA_WIDTH-1:0] s_imm;
    wire [`ysyx_040991_DATA_WIDTH-1:0] i_imm;
    wire [`ysyx_040991_DATA_WIDTH-1:0] z_imm;
    assign u_imm = {{32{es_inst[31]}}, es_inst[31:12], 12'b0};
    assign s_imm = {{52{es_inst[31]}}, es_inst[31:25], es_inst[11:7]};
    assign i_imm = {{52{es_inst[31]}}, es_inst[31:20]};
    assign z_imm = {59'b0, es_inst[19:15]};
    assign es_alu_src1  =   ({`ysyx_040991_DATA_WIDTH{es_src1_sel[0]}} & es_rs1_data)
                        |   ({`ysyx_040991_DATA_WIDTH{es_src1_sel[1]}} & u_imm      )
                        |   ({`ysyx_040991_DATA_WIDTH{es_src1_sel[2]}} & z_imm      )
                        |   ({`ysyx_040991_DATA_WIDTH{es_src1_sel[3]}} & ~es_rs1_data)
                        |   ({`ysyx_040991_DATA_WIDTH{es_src1_sel[4]}} & ~z_imm      );


    assign es_alu_src2  =   ({`ysyx_040991_DATA_WIDTH{es_src2_sel[0]}} & es_pc          ) 
                        |   ({`ysyx_040991_DATA_WIDTH{es_src2_sel[1]}} & i_imm          )
                        |   ({`ysyx_040991_DATA_WIDTH{es_src2_sel[2]}} & s_imm          )
                        |   ({`ysyx_040991_DATA_WIDTH{es_src2_sel[3]}} & es_rs2_data    )
                        |   ({`ysyx_040991_DATA_WIDTH{es_src2_sel[4]}} & es_csr_rdata_final   );
    assign es_csr_raddr = es_inst[31:20];

    // csr bypassing logic   

    wire [2:0] es_csr_rdata_sel;
    wire ms_csr_wen;
    wire [11:0] ms_csr_waddr;
    wire [63:0] ms_csr_wdata;
    wire ws_csr_wen;
    wire [11:0] ws_csr_waddr;
    wire [63:0] ws_csr_wdata;
    assign {ms_csr_wen, ms_csr_waddr, ms_csr_wdata} = ms_csr_forwarding_bus;
    assign {ws_csr_wen, ws_csr_waddr, ws_csr_wdata} = ws_csr_forwarding_bus;
    ysyx_040991_csr_forwarding_unit u_csr_forwarding_unit(
    	.ms_csr_wen       (ms_csr_wen       ),
        .ms_csr_waddr     (ms_csr_waddr     ),
        .ws_csr_wen       (ws_csr_wen       ),
        .ws_csr_waddr     (ws_csr_waddr     ),
        .es_csr_raddr     (es_csr_raddr     ),
        .es_csr_rdata_sel (es_csr_rdata_sel )
    );
    
    assign es_csr_rdata_final = ({`ysyx_040991_DATA_WIDTH{es_csr_rdata_sel[0]}} & es_csr_rdata  )
                            |   ({`ysyx_040991_DATA_WIDTH{es_csr_rdata_sel[1]}} & ms_csr_wdata  )
                            |   ({`ysyx_040991_DATA_WIDTH{es_csr_rdata_sel[2]}} & ws_csr_wdata  );

    ysyx_040991_alu #(
        .ysyx_040991_DATA_WIDTH (`ysyx_040991_DATA_WIDTH    ),
        .ALU_WIDTH  (11             )
    ) u_alu (
        .alu_src1   (es_alu_src1    ),
        .alu_src2   (es_alu_src2    ),
        .word_sel   (es_word_op     ),
        .alu_op     (es_alu_op      ),
        .alu_result (es_alu_result  )
    );


    // data cache write signals
    assign dcache_valid     = es_data_ram_en && es_valid && !es_is_exception && !(ws_flush || ws_flush_r) && !ms_is_exception;  // do not store when ms or ws is exception
    assign dcache_op        = es_data_ram_wen;
    assign {dcache_tag, dcache_index, dcache_offset} = es_alu_result[31:0];
    assign dcache_wstrb         =   ({8{es_save_op[3]}} & (({8{dcache_offset[2:0]==3'b000}}  & 8'b1        )    // inst_sb
                                                        |  ({8{dcache_offset[2:0]==3'b001}}  & 8'b10       )
                                                        |  ({8{dcache_offset[2:0]==3'b010}}  & 8'b100      )
                                                        |  ({8{dcache_offset[2:0]==3'b011}}  & 8'b1000     )
                                                        |  ({8{dcache_offset[2:0]==3'b100}}  & 8'b10000    )
                                                        |  ({8{dcache_offset[2:0]==3'b101}}  & 8'b100000   )
                                                        |  ({8{dcache_offset[2:0]==3'b110}}  & 8'b1000000  )
                                                        |  ({8{dcache_offset[2:0]==3'b111}}  & 8'b10000000 )))
                                        // inst_sh 
                                |   ({8{es_save_op[2]}} & (({8{dcache_offset[2:1]==2'b00 }}  & 8'b11       )   
                                                        |  ({8{dcache_offset[2:1]==2'b01 }}  & 8'b1100     )
                                                        |  ({8{dcache_offset[2:1]==2'b10 }}  & 8'b110000   )
                                                        |  ({8{dcache_offset[2:1]==2'b11 }}  & 8'b11000000 )))
                                // inst_sw
                                |   ({8{es_save_op[1]}} & (({8{dcache_offset[2] == 1'b0  }}  & 8'b1111     ) 
                                                        |  ({8{dcache_offset[2] == 1'b1  }}  & 8'b11110000 )))
                                // inst_sd                
                                |   ({8{es_save_op[0]}} & 8'b11111111);

    assign dcache_wdata     =   ({`ysyx_040991_DATA_WIDTH{es_save_op[3]}} & {8{es_rs2_data[7:0]}})   // inst_sb
                            |   ({`ysyx_040991_DATA_WIDTH{es_save_op[2]}} & {4{es_rs2_data[15:0]}})   // inst_sh
                            |   ({`ysyx_040991_DATA_WIDTH{es_save_op[1]}} & {2{es_rs2_data[31:0]}})   // inst_sw
                            |   ({`ysyx_040991_DATA_WIDTH{es_save_op[0]}} & es_rs2_data); 

    assign es_branch_bus    =   {es_jalr_en && es_valid, es_alu_result};

    assign es_fence = es_fence_i;

    // exception
    wire es_exception_en;
    wire [5:0] es_exception_next;
    assign es_exception_en = |es_exception_next[5:4];
    assign es_exception_next[5:4] = 2'b0;
    assign es_exception_next[3:0] = 4'b0;
    assign es_to_ms_exception = es_exception_en ? es_exception_next : es_exception_curr;

    // debug infomation


    assign dcache_uncache = ((es_alu_result & 64'hf000_0000) != 64'h8000_0000) && es_data_ram_en && es_valid;

endmodule

module ysyx_040991_forwarding_unit(
    // exe to id
    input es_rd_wen,
    input [4:0] es_rd_addr,

    // mem to id
    input ms_rd_wen,
    input [4:0] ms_rd_addr,

    // wb to id
    input ws_rd_wen,
    input [4:0] ws_rd_addr,

    // id signals
    input [4:0] ds_rs1_addr,
    input [4:0] ds_rs2_addr,
    output [3:0] ds_rs1_forward_sel,
    output [3:0] ds_rs2_forward_sel
);


    wire es_rd_bypass1;
    wire ms_rd_bypass1;
    wire ws_rd_bypass1;
    assign es_rd_bypass1 = es_rd_wen && |es_rd_addr && (es_rd_addr == ds_rs1_addr);
    assign ms_rd_bypass1 = ms_rd_wen && |ms_rd_addr && (ms_rd_addr == ds_rs1_addr);
    assign ws_rd_bypass1 = ws_rd_wen && |ws_rd_addr && (ws_rd_addr == ds_rs1_addr);

    // [0] rf_rdata1 
    assign ds_rs1_forward_sel[0]    =   ~es_rd_bypass1 
                                    &&  ~ms_rd_bypass1 
                                    &&  ~ws_rd_bypass1; 
    // [1] es result1
    assign ds_rs1_forward_sel[1]    =    es_rd_bypass1; 
    // [2] ms result1
    assign ds_rs1_forward_sel[2]    =   ~es_rd_bypass1 
                                    &&   ms_rd_bypass1;                             
    // [3] ws result1
    assign ds_rs1_forward_sel[3]    =   ~es_rd_bypass1 
                                    &&  ~ms_rd_bypass1 
                                    &&   ws_rd_bypass1; 
    wire es_rd_bypass2;
    wire ms_rd_bypass2;
    wire ws_rd_bypass2;
    assign es_rd_bypass2 = es_rd_wen && |es_rd_addr && (es_rd_addr == ds_rs2_addr);
    assign ms_rd_bypass2 = ms_rd_wen && |ms_rd_addr && (ms_rd_addr == ds_rs2_addr);
    assign ws_rd_bypass2 = ws_rd_wen && |ws_rd_addr && (ws_rd_addr == ds_rs2_addr);

    // [0] rf_rdata2 
    assign ds_rs2_forward_sel[0]    =   ~es_rd_bypass2 
                                    &&  ~ms_rd_bypass2 
                                    &&  ~ws_rd_bypass2; 
    // [1] es result2
    assign ds_rs2_forward_sel[1]    =    es_rd_bypass2; 
    // [2] ms result2
    assign ds_rs2_forward_sel[2]    =   ~es_rd_bypass2 
                                    &&   ms_rd_bypass2;                             
    // [3] ws result2
    assign ds_rs2_forward_sel[3]    =   ~es_rd_bypass2 
                                    &&  ~ms_rd_bypass2 
                                    &&   ws_rd_bypass2; 

endmodule

module ysyx_040991_id_stage(
    input                          clk           ,
    input                          rst         ,
    input                          interrupt_taken,
    //allowin
    input                          es_allowin    ,
    output                         ds_allowin    ,
    //from fs
    input                          fs_to_ds_valid,
    input  [`ysyx_040991_FS_TO_DS_BUS_WD -1:0] fs_to_ds_bus  ,
    input  [`ysyx_040991_EXCEPTION_WD-1:0] fs_to_ds_exception,
    //to es
    output                         ds_to_es_valid,
    output [`ysyx_040991_DS_TO_ES_BUS_WD -1:0] ds_to_es_bus  ,
    output [`ysyx_040991_EXCEPTION_WD-1:0] ds_to_es_exception,
    //to ctrl
    output [`ysyx_040991_BR_BUS_WD         :0] ds_branch_bus,
    input ws_flush,
    //to rf: for write back
    input  [`ysyx_040991_WS_TO_RF_BUS_WD -1:0] ws_to_rf_bus,

    // forwarding signals
    input  [`ysyx_040991_ES_FORWARDING_BUS_WD -1:0] es_forwarding_bus,
    input  [`ysyx_040991_MS_FORWARDING_BUS_WD -1:0] ms_forwarding_bus,
    input  [`ysyx_040991_WS_FORWARDING_BUS_WD -1:0] ws_forwarding_bus

    // from csr
    // input [1:0] priv_i
);

    reg         ds_valid   ;
    wire        ds_ready_go;

    reg  [`ysyx_040991_FS_TO_DS_BUS_WD -1:0] fs_to_ds_bus_r;
    wire [31:0] ds_inst;          
    wire [`ysyx_040991_VADDR_WIDTH-1:0] ds_pc;
    assign {
        ds_inst,
        ds_pc  
    } = fs_to_ds_bus_r;
    wire [5:0] ds_exception_curr;
    reg  [`ysyx_040991_EXCEPTION_WD-1:0] fs_to_ds_exception_r;
    assign ds_exception_curr = fs_to_ds_exception_r;
    wire        rf_wen   ;
    wire [ 4:0] rf_waddr;
    wire [`ysyx_040991_DATA_WIDTH-1:0] rf_wdata;
    assign {
        rf_wen      ,
        rf_waddr    ,  
        rf_wdata   
    } = ws_to_rf_bus;
        
    wire [10:0] alu_op;
    wire [4:0] src1_sel;
    wire [4:0] src2_sel;
    wire [6:0] load_op;
    wire [3:0] save_op;
    wire [3:0] mul_op;
    wire [3:0] div_op;
    wire word_op;
    wire [5:0] wb_sel;
    wire rd_wen; // ds stage
    wire [ 4:0] rf_raddr1;
    wire [ 4:0] rf_raddr2;
    wire [`ysyx_040991_DATA_WIDTH-1:0] rs1_data;
    wire [`ysyx_040991_DATA_WIDTH-1:0] rs2_data;
    
    wire [4:0] rd_addr;
    wire [4:0] rs1_addr;
    wire [4:0] rs2_addr;

    wire data_ram_en;
    wire data_ram_wen;

    wire [3:0] rs1_forward_sel;
    wire [3:0] rs2_forward_sel;
    wire es_rd_wen;
    wire [4:0] es_rd_addr;
    wire [`ysyx_040991_DATA_WIDTH-1:0] es_rd_data;
    wire ms_rd_wen;
    wire [4:0] ms_rd_addr;
    wire [`ysyx_040991_DATA_WIDTH-1:0] ms_rd_data;
    wire ws_rd_wen;
    wire [4:0] ws_rd_addr;
    wire [`ysyx_040991_DATA_WIDTH-1:0] ws_rd_data;
    wire es_ds_stall;
    wire ms_ds_stall;
    wire ds_stall;
    wire fence;
    assign {
        es_ds_stall,
        es_rd_wen,
        es_rd_addr,
        es_rd_data
    } = es_forwarding_bus;
    assign {
        ms_ds_stall,
        ms_rd_wen,
        ms_rd_addr,
        ms_rd_data
    } = ms_forwarding_bus;
    assign {
        ws_rd_wen,
        ws_rd_addr,
        ws_rd_data
    } = ws_forwarding_bus;
    wire jalr_en;
    wire csr_wen;
    assign ds_to_es_bus = {
        fence       , 
        alu_op      ,  
        src1_sel    ,
        src2_sel    ,
        load_op     ,
        save_op     ,
        mul_op      ,
        div_op      ,
        word_op     ,
        wb_sel      ,
        rd_wen      ,
        data_ram_en ,
        data_ram_wen,
        rs1_data    ,
        rs2_data    ,
        rd_addr     ,
        jalr_en     ,
        csr_wen     ,
        ds_stall    ,
        ds_pc       ,
        ds_inst     
    };

    reg ws_flush_r;
    always @(posedge clk) begin
        if (rst) begin
            ws_flush_r <= 1'b0;
        end else if (ws_flush) begin
            ws_flush_r <= 1'b1;
        end else if (fs_to_ds_valid & ds_allowin) begin
            ws_flush_r <= 1'b0;
        end
    end

    assign ds_ready_go    =     !(ms_ds_stall && (ms_rd_addr == rs1_addr || ms_rd_addr == rs2_addr))
                            &&  !(es_ds_stall && (es_rd_addr == rs1_addr || es_rd_addr == rs2_addr)); // stall when load mul div in exe stage
    assign ds_allowin     = !ds_valid || ds_ready_go && es_allowin;
    assign ds_to_es_valid = ds_valid && ds_ready_go && !(ws_flush || ws_flush_r);

    always @(posedge clk) begin
        if (rst) begin
            ds_valid <= 1'b0;
        end
        else if (ds_allowin) begin
            ds_valid <= fs_to_ds_valid;
        end
        if (fs_to_ds_valid && ds_allowin) begin
            fs_to_ds_bus_r <= fs_to_ds_bus;
            fs_to_ds_exception_r <= fs_to_ds_exception;
        end
    end


    wire [2:0] funct3;
    wire [6:0] opcode;
    wire [6:0] funct7;
    assign funct3   = ds_inst[14:12];
    assign opcode   = ds_inst[6:0];
    assign funct7   = ds_inst[31:25];
    assign rd_addr  = ds_inst[11:7];
    assign rs1_addr = ds_inst[19:15];
    assign rs2_addr = ds_inst[24:20];

    wire [7:0] funct3_d;
    wire [127:0] opcode_d;
    wire [127:0] funct7_d;
    ysyx_040991_decoder_3_8     u_dec0(.in(funct3), .out(funct3_d));
    ysyx_040991_decoder_7_128   u_dec1(.in(opcode), .out(opcode_d));
    ysyx_040991_decoder_7_128   u_dec2(.in(funct7), .out(funct7_d));

    // RV32I 
    // U type
    wire inst_lui, inst_auipc;
    assign inst_lui     =   opcode_d[7'b0110111];
    assign inst_auipc   =   opcode_d[7'b0010111];
    // J type & I type
    wire inst_jal, inst_jalr;
    assign inst_jal     =   opcode_d[7'b1101111];
    assign inst_jalr    =   opcode_d[7'b1100111] & funct3_d[3'b000];
    // B type
    wire inst_beq, inst_bne, inst_blt, inst_bge, inst_bltu, inst_bgeu;
    assign inst_beq     =   opcode_d[7'b1100011] & funct3_d[3'b000];
    assign inst_bne     =   opcode_d[7'b1100011] & funct3_d[3'b001];
    assign inst_blt     =   opcode_d[7'b1100011] & funct3_d[3'b100];
    assign inst_bge     =   opcode_d[7'b1100011] & funct3_d[3'b101];
    assign inst_bltu    =   opcode_d[7'b1100011] & funct3_d[3'b110];
    assign inst_bgeu    =   opcode_d[7'b1100011] & funct3_d[3'b111];
    // I type
    wire inst_lb, inst_lh, inst_lw, inst_lbu, inst_lhu;
    assign inst_lb      =   opcode_d[7'b0000011] & funct3_d[3'b000];
    assign inst_lh      =   opcode_d[7'b0000011] & funct3_d[3'b001];
    assign inst_lw      =   opcode_d[7'b0000011] & funct3_d[3'b010];
    assign inst_lbu     =   opcode_d[7'b0000011] & funct3_d[3'b100];
    assign inst_lhu     =   opcode_d[7'b0000011] & funct3_d[3'b101];
    // S type
    wire inst_sb, inst_sh, inst_sw;
    assign inst_sb      =   opcode_d[7'b0100011] & funct3_d[3'b000];
    assign inst_sh      =   opcode_d[7'b0100011] & funct3_d[3'b001];
    assign inst_sw      =   opcode_d[7'b0100011] & funct3_d[3'b010];
    // I type
    wire inst_addi, inst_slti, inst_sltiu, inst_xori, inst_ori, inst_andi, inst_slli, inst_srli, inst_srai;
    assign inst_addi    =   opcode_d[7'b0010011] & funct3_d[3'b000];
    assign inst_slti    =   opcode_d[7'b0010011] & funct3_d[3'b010];
    assign inst_sltiu   =   opcode_d[7'b0010011] & funct3_d[3'b011];
    assign inst_xori    =   opcode_d[7'b0010011] & funct3_d[3'b100];
    assign inst_ori     =   opcode_d[7'b0010011] & funct3_d[3'b110];
    assign inst_andi    =   opcode_d[7'b0010011] & funct3_d[3'b111];
    assign inst_slli    =   opcode_d[7'b0010011] & funct3_d[3'b001] & ~|ds_inst[31:26]; // shamt[25:20]
    assign inst_srli    =   opcode_d[7'b0010011] & funct3_d[3'b101] & ~|ds_inst[31:26]; // shamt[25:20]
    assign inst_srai    =   opcode_d[7'b0010011] & funct3_d[3'b101] & ~ds_inst[31] & ds_inst[30] & ~|ds_inst[29:26]; // shamt[25:20]
    // R type
    wire inst_add, inst_sub, inst_sll, inst_slt, inst_sltu, inst_xor, inst_srl, inst_sra, inst_or, inst_and;
    assign inst_add     =   opcode_d[7'b0110011] & funct3_d[3'b000] & funct7_d[7'b0000000];
    assign inst_sub     =   opcode_d[7'b0110011] & funct3_d[3'b000] & funct7_d[7'b0100000];
    assign inst_sll     =   opcode_d[7'b0110011] & funct3_d[3'b001] & funct7_d[7'b0000000];
    assign inst_slt     =   opcode_d[7'b0110011] & funct3_d[3'b010] & funct7_d[7'b0000000];
    assign inst_sltu    =   opcode_d[7'b0110011] & funct3_d[3'b011] & funct7_d[7'b0000000];
    assign inst_xor     =   opcode_d[7'b0110011] & funct3_d[3'b100] & funct7_d[7'b0000000];
    assign inst_srl     =   opcode_d[7'b0110011] & funct3_d[3'b101] & funct7_d[7'b0000000];
    assign inst_sra     =   opcode_d[7'b0110011] & funct3_d[3'b101] & funct7_d[7'b0100000];
    assign inst_or      =   opcode_d[7'b0110011] & funct3_d[3'b110] & funct7_d[7'b0000000];
    assign inst_and     =   opcode_d[7'b0110011] & funct3_d[3'b111] & funct7_d[7'b0000000];
    // csr instructions I type
    wire inst_csrrw, inst_csrrs, inst_csrrc, inst_csrrwi, inst_csrrsi, inst_csrrci;
    assign inst_csrrw   =   opcode_d[7'b1110011] & funct3_d[3'b001];
    assign inst_csrrs   =   opcode_d[7'b1110011] & funct3_d[3'b010];
    assign inst_csrrc   =   opcode_d[7'b1110011] & funct3_d[3'b011];
    assign inst_csrrwi  =   opcode_d[7'b1110011] & funct3_d[3'b101];
    assign inst_csrrsi  =   opcode_d[7'b1110011] & funct3_d[3'b110];
    assign inst_csrrci  =   opcode_d[7'b1110011] & funct3_d[3'b111];
   
    // others
    wire inst_ebreak, inst_ecall, inst_mret;
    assign inst_ebreak  =   opcode_d[7'b1110011] & ~|ds_inst[31:21] & ds_inst[20] & ~|ds_inst[19:7];
    assign inst_ecall   =   opcode_d[7'b1110011] & ~|ds_inst[31:7];
    assign inst_mret    =   opcode_d[7'b1110011] & funct7_d[7'b0011000] & ~|ds_inst[20:7] & ds_inst[21] & ~|ds_inst[24:22];
    wire inst_fence_i;
    assign inst_fence_i =   opcode_d[7'b0001111] & ~|ds_inst[31:13] & ds_inst[12] & ~|ds_inst[11:7];
    
    
    // RV64I 
    // S type
    wire inst_sd;
    assign inst_sd      =   opcode_d[7'b0100011] & funct3_d[3'b011];
    // I type
    wire inst_ld, inst_lwu;
    assign inst_ld      =   opcode_d[7'b0000011] & funct3_d[3'b011];
    assign inst_lwu     =   opcode_d[7'b0000011] & funct3_d[3'b110];
    wire inst_addiw, inst_slliw, inst_srliw, inst_sraiw;
    assign inst_addiw   =   opcode_d[7'b0011011] & funct3_d[3'b000];
    assign inst_slliw   =   opcode_d[7'b0011011] & funct3_d[3'b001] & funct7_d[7'b0000000]; // shamt[24:20]
    assign inst_srliw   =   opcode_d[7'b0011011] & funct3_d[3'b101] & funct7_d[7'b0000000]; // shamt[24:20]
    assign inst_sraiw   =   opcode_d[7'b0011011] & funct3_d[3'b101] & funct7_d[7'b0100000]; // shamt[24:20]
    // R type
    wire inst_addw, inst_subw, inst_sllw, inst_srlw, inst_sraw;
    assign inst_addw    =   opcode_d[7'b0111011] & funct3_d[3'b000] & funct7_d[7'b0000000];
    assign inst_subw    =   opcode_d[7'b0111011] & funct3_d[3'b000] & funct7_d[7'b0100000];
    assign inst_sllw    =   opcode_d[7'b0111011] & funct3_d[3'b001] & funct7_d[7'b0000000];
    assign inst_srlw    =   opcode_d[7'b0111011] & funct3_d[3'b101] & funct7_d[7'b0000000];
    assign inst_sraw    =   opcode_d[7'b0111011] & funct3_d[3'b101] & funct7_d[7'b0100000];

    // RV32M
    // R type
    wire inst_mul, inst_mulh, inst_mulhsu, inst_mulhu, inst_div, inst_divu, inst_rem, inst_remu;
    assign inst_mul     =   opcode_d[7'b0110011] & funct3_d[3'b000] & funct7_d[7'b0000001];
    assign inst_mulh    =   opcode_d[7'b0110011] & funct3_d[3'b001] & funct7_d[7'b0000001];
    assign inst_mulhsu  =   opcode_d[7'b0110011] & funct3_d[3'b010] & funct7_d[7'b0000001];
    assign inst_mulhu   =   opcode_d[7'b0110011] & funct3_d[3'b011] & funct7_d[7'b0000001];
    assign inst_div     =   opcode_d[7'b0110011] & funct3_d[3'b100] & funct7_d[7'b0000001];
    assign inst_divu    =   opcode_d[7'b0110011] & funct3_d[3'b101] & funct7_d[7'b0000001];
    assign inst_rem     =   opcode_d[7'b0110011] & funct3_d[3'b110] & funct7_d[7'b0000001];
    assign inst_remu    =   opcode_d[7'b0110011] & funct3_d[3'b111] & funct7_d[7'b0000001];

    // RV64M
    // R type
    wire inst_mulw, inst_divw, inst_divuw, inst_remw, inst_remuw;
    assign inst_mulw   =   opcode_d[7'b0111011] & funct3_d[3'b000] & funct7_d[7'b0000001];
    assign inst_divw   =   opcode_d[7'b0111011] & funct3_d[3'b100] & funct7_d[7'b0000001];
    assign inst_divuw  =   opcode_d[7'b0111011] & funct3_d[3'b101] & funct7_d[7'b0000001];
    assign inst_remw   =   opcode_d[7'b0111011] & funct3_d[3'b110] & funct7_d[7'b0000001];
    assign inst_remuw  =   opcode_d[7'b0111011] & funct3_d[3'b111] & funct7_d[7'b0000001];

    // alu_op [0]add [1]sub [2]slt [3]sltu [4]and [5]nop [6]or [7]xor [8]sll [9]srl [10]sra
    assign alu_op[ 0]   = inst_add  | inst_addi |  inst_addw | inst_addiw 
                        | inst_lb   | inst_lh   | inst_lw | inst_lbu | inst_lhu | inst_lwu | inst_ld
                        | inst_sb   | inst_sh   | inst_sw | inst_sd
                        | inst_jalr | inst_auipc;
    assign alu_op[ 1]   = inst_sub  | inst_subw;
    assign alu_op[ 2]   = inst_slt  | inst_slti;
    assign alu_op[ 3]   = inst_sltu | inst_sltiu;
    assign alu_op[ 4]   = inst_and  | inst_andi  | inst_csrrc | inst_csrrci;
    assign alu_op[ 5]   = inst_lui  | inst_csrrw | inst_csrrwi; 
    assign alu_op[ 6]   = inst_or   | inst_ori   | inst_csrrs | inst_csrrsi; 
    assign alu_op[ 7]   = inst_xor  | inst_xori;
    assign alu_op[ 8]   = inst_sll  | inst_slli | inst_sllw | inst_slliw;
    assign alu_op[ 9]   = inst_srl  | inst_srli | inst_srlw | inst_srliw;
    assign alu_op[10]   = inst_sra  | inst_srai | inst_sraw | inst_sraiw;

    // src1_sel [4]~z_imm [3]~reg1  [2]z_imm [1]u_imm [0]reg1
    assign src1_sel[4]  = inst_csrrci;
    assign src1_sel[3]  = inst_csrrc;
    assign src1_sel[2]  = inst_csrrwi | inst_csrrsi;
    assign src1_sel[1]  = inst_lui | inst_auipc;
    assign src1_sel[0]  = inst_lb | inst_lh | inst_lw | inst_lbu | inst_lhu | inst_lwu | inst_ld
                        | inst_sb | inst_sh | inst_sw | inst_sd
                        | inst_jalr
                        | inst_addi | inst_slti | inst_sltiu | inst_xori | inst_ori | inst_andi | inst_slli | inst_srli | inst_srai
                        | inst_add | inst_sub | inst_sll | inst_slt | inst_sltu | inst_xor | inst_srl | inst_sra | inst_or | inst_and
                        | inst_addw | inst_subw | inst_sllw | inst_srlw | inst_sraw
                        | inst_addiw | inst_slliw | inst_srliw | inst_sraiw
                        | inst_mul | inst_mulh | inst_mulhsu | inst_mulhu | inst_div | inst_divu | inst_rem | inst_remu
                        | inst_mulw | inst_divw | inst_divuw | inst_remw | inst_remuw
                        | inst_csrrw | inst_csrrs;

    // src2_sel [4]csr_rdata [3]reg2 [2]s_imm [1]i_imm [0]pc
    assign src2_sel[4]  = inst_csrrs | inst_csrrsi | inst_csrrc | inst_csrrci;
    assign src2_sel[3]  = inst_add | inst_sub | inst_sll | inst_slt | inst_sltu | inst_xor | inst_srl | inst_sra | inst_or | inst_and
                        | inst_addw | inst_subw | inst_sllw | inst_srlw | inst_sraw
                        | inst_mul | inst_mulh | inst_mulhsu | inst_mulhu | inst_div | inst_divu | inst_rem | inst_remu
                        | inst_mulw | inst_divw | inst_divuw | inst_remw | inst_remuw;
    assign src2_sel[2]  = inst_sb | inst_sh | inst_sw | inst_sd;
    assign src2_sel[1]  = inst_lb | inst_lh | inst_lw | inst_lbu | inst_lhu | inst_lwu | inst_ld
                        | inst_addi | inst_slti | inst_sltiu | inst_xori | inst_ori | inst_andi | inst_slli | inst_srli | inst_srai
                        | inst_addiw | inst_slliw | inst_srliw | inst_sraiw
                        | inst_jalr;
    assign src2_sel[0]  = inst_auipc;

    // wb_sel[5]div result [4]mul result [3]data ram dout [2]alu result [1]snpc [0]csr_rdata
    assign wb_sel[5]    = inst_div | inst_divu | inst_rem | inst_remu | inst_divw | inst_divuw | inst_remw | inst_remuw; 
    assign wb_sel[4]    = inst_mul | inst_mulh | inst_mulhsu | inst_mulhu | inst_mulw;
    assign wb_sel[3]    = inst_lb | inst_lh | inst_lw | inst_lbu | inst_lhu | inst_lwu | inst_ld;
    assign wb_sel[2]    = inst_lui | inst_auipc
                        | inst_addi | inst_slti | inst_sltiu | inst_xori | inst_ori | inst_andi | inst_slli | inst_srli | inst_srai
                        | inst_add | inst_sub | inst_sll | inst_slt | inst_sltu | inst_xor | inst_srl | inst_sra | inst_or | inst_and
                        | inst_addw | inst_subw | inst_sllw | inst_srlw | inst_sraw
                        | inst_addiw | inst_slliw | inst_srliw | inst_sraiw;
    assign wb_sel[1] =  inst_jal | inst_jalr;
    assign wb_sel[0] =  inst_csrrw | inst_csrrs | inst_csrrc | inst_csrrwi | inst_csrrsi | inst_csrrci;


    assign data_ram_en  =   inst_lb | inst_lh | inst_lw | inst_lbu | inst_lhu | inst_lwu | inst_ld 
                        |   inst_sb | inst_sh | inst_sw | inst_sd;

    assign data_ram_wen =   inst_sb | inst_sh | inst_sw | inst_sd;

    assign word_op      =   inst_addw | inst_subw | inst_sllw | inst_srlw | inst_sraw
                        |   inst_addiw | inst_slliw | inst_srliw | inst_sraiw
                        |   inst_mulw | inst_divw | inst_divuw | inst_remw | inst_remuw;

    assign load_op[6] = inst_lb;  // 8bit sign extend
    assign load_op[5] = inst_lh;  // 16bit sign extend
    assign load_op[4] = inst_lw;  // 32bit sign extend 
    assign load_op[3] = inst_ld;  // 64bit 
    assign load_op[2] = inst_lbu; // 8bit zero extend
    assign load_op[1] = inst_lhu; // 16bit zero extend
    assign load_op[0] = inst_lwu; // 32bit zero extend

    assign save_op[3] = inst_sb; // 8bit
    assign save_op[2] = inst_sh; // 16bit
    assign save_op[1] = inst_sw; // 32bit
    assign save_op[0] = inst_sd; // 64bit
        

    assign mul_op[3] = inst_mul | inst_mulw;
    assign mul_op[2] = inst_mulh;
    assign mul_op[1] = inst_mulhsu;
    assign mul_op[0] = inst_mulhu;


    assign div_op[3] = inst_div | inst_divw;
    assign div_op[2] = inst_divu | inst_divuw;
    assign div_op[1] = inst_rem | inst_remw;
    assign div_op[0] = inst_remu | inst_remuw;
                            
    assign rd_wen   =   inst_addi | inst_slti | inst_sltiu | inst_xori | inst_ori | inst_andi | inst_slli | inst_srli | inst_srai
                    |   inst_add | inst_sub | inst_sll | inst_slt | inst_sltu | inst_xor | inst_srl | inst_sra | inst_or | inst_and
                    |   inst_addw | inst_subw | inst_sllw | inst_srlw | inst_sraw
                    |   inst_addiw | inst_slliw | inst_srliw | inst_sraiw 
                    |   inst_lb | inst_lh | inst_lw | inst_lbu | inst_lhu | inst_lwu | inst_ld
                    |   inst_jal | inst_jalr
                    |   inst_lui | inst_auipc
                    |   inst_mul | inst_mulh | inst_mulhsu | inst_mulhu | inst_div | inst_divu | inst_rem | inst_remu
                    |   inst_mulw | inst_divw | inst_divuw | inst_remw | inst_remuw
                    |   inst_csrrw | inst_csrrs | inst_csrrc | inst_csrrwi | inst_csrrsi | inst_csrrci;

    // id stage stall
    assign ds_stall = inst_lb | inst_lh | inst_lw | inst_lbu | inst_lhu | inst_lwu | inst_ld
                    |   inst_mul | inst_mulh | inst_mulhsu | inst_mulhu | inst_div | inst_divu | inst_rem | inst_remu
                    |   inst_mulw | inst_divw | inst_divuw | inst_remw | inst_remuw;

    // fence.i instruction
    assign fence    = inst_fence_i;


    assign rf_raddr1 = rs1_addr;
    assign rf_raddr2 = rs2_addr;
    wire [`ysyx_040991_DATA_WIDTH-1:0] rf_rdata1;
    wire [`ysyx_040991_DATA_WIDTH-1:0] rf_rdata2;
    assign rs1_data = ({64{rs1_forward_sel[0]}} & rf_rdata1)
                    | ({64{rs1_forward_sel[1]}} & es_rd_data)
                    | ({64{rs1_forward_sel[2]}} & ms_rd_data)
                    | ({64{rs1_forward_sel[3]}} & ws_rd_data);

    assign rs2_data = ({64{rs2_forward_sel[0]}} & rf_rdata2)
                    | ({64{rs2_forward_sel[1]}} & es_rd_data)
                    | ({64{rs2_forward_sel[2]}} & ms_rd_data)
                    | ({64{rs2_forward_sel[3]}} & ws_rd_data);

    ysyx_040991_RegisterFile #(
        .ysyx_040991_DATA_WIDTH(`ysyx_040991_DATA_WIDTH)
    ) regfile (
        .clk(clk),
        .raddr1(rf_raddr1),
        .raddr2(rf_raddr2),
        .wb_data(rf_wdata),
        .wb_addr(rf_waddr),
        .wen(rf_wen),
        .rdata1(rf_rdata1),
        .rdata2(rf_rdata2)
    );


    wire br_eq, br_lt, br_ltu;
    ysyx_040991_BranchCondGen #(
        .ysyx_040991_DATA_WIDTH(`ysyx_040991_DATA_WIDTH)
    ) u_br_gen (
        .reg1(rs1_data),
        .reg2(rs2_data),
        .br_eq(br_eq),
        .br_lt(br_lt),
        .br_ltu(br_ltu)
    );

    wire branch_sel;
    assign branch_sel   =   (inst_beq   &   br_eq   )
                        |   (inst_bne   &   !br_eq  )
                        |   (inst_blt   &   br_lt   )
                        |   (inst_bge   &   !br_lt  )
                        |   (inst_bltu  &   br_ltu  )
                        |   (inst_bgeu  &   !br_ltu );

    wire [`ysyx_040991_DATA_WIDTH-1:0] b_imm;
    wire [`ysyx_040991_DATA_WIDTH-1:0] j_imm;
    assign j_imm = {{43{ds_inst[31]}}, ds_inst[31], ds_inst[19:12], ds_inst[20], ds_inst[30:21], 1'b0};
    assign b_imm = {{51{ds_inst[31]}}, ds_inst[31], ds_inst[7], ds_inst[30:25], ds_inst[11:8], 1'b0};

    wire [`ysyx_040991_VADDR_WIDTH-1:0] br_j_pc;
    wire                    br_j_en;
    assign br_j_pc  = ds_pc + (({64{inst_jal}}    &   j_imm)      // jal pc
                            |  ({64{branch_sel}}  &   b_imm));    // branch pc
    assign br_j_en  = (inst_jal || branch_sel) && ds_valid;
    assign jalr_en = inst_jalr; // to exe stage
    

    wire br_stall;
    assign br_stall = (inst_beq | inst_bne | inst_blt| inst_bltu | inst_bge | inst_bgeu | inst_jal) & !ds_ready_go;


    assign ds_branch_bus = {br_stall, br_j_en, br_j_pc};

    // csr signals
    assign csr_wen = inst_csrrw | inst_csrrs | inst_csrrc | inst_csrrwi | inst_csrrsi | inst_csrrci;

    wire ds_exception_en;
    wire [5:0] ds_exception_next;
    assign ds_exception_en = |ds_exception_next[5:4];
    assign ds_exception_next[5:4] = ({2{interrupt_taken         }} & `ysyx_040991_EXCEPTION_INTERRUPT) // interrupt
                                |   ({2{inst_ebreak | inst_ecall}} & `ysyx_040991_EXCEPTION_EXCEPTION) // exception
                                |   ({2{inst_mret               }} & `ysyx_040991_EXCEPTION_ERET); // ret

    assign ds_exception_next[3:0] = ({4{interrupt_taken         }} & {`ysyx_040991_IRQ_M_TIMER} )   // interrupt only tmier interrupt
                                |   ({4{inst_ebreak             }} & `ysyx_040991_EXCEPTION_BREAKPOINT  )   // breakpoint
                                |   ({4{inst_ecall              }} & `ysyx_040991_EXCEPTION_ECALL_M     )   // ecall
                                |   ({4{inst_mret               }} & {2'b0, `ysyx_040991_PRIV_MACHINE}  );  // ret

    assign ds_to_es_exception = ds_exception_en ? ds_exception_next : ds_exception_curr;

    // forwarding logic
    ysyx_040991_forwarding_unit u_forward (
        .es_rd_wen(es_rd_wen),
        .es_rd_addr(es_rd_addr),
        .ms_rd_wen(ms_rd_wen),
        .ms_rd_addr(ms_rd_addr),
        .ws_rd_wen(ws_rd_wen),
        .ws_rd_addr(ws_rd_addr),
        .ds_rs1_addr(rs1_addr),
        .ds_rs2_addr(rs2_addr),
        .ds_rs1_forward_sel(rs1_forward_sel),
        .ds_rs2_forward_sel(rs2_forward_sel)
    );

endmodule

module ysyx_040991_if_stage (
    input                          clk              ,     
    input                          rst              ,
    //allowin
    input                          ds_allowin       ,
    // from ctrl branch bus
    input  [`ysyx_040991_BR_BUS_WD         :0] br_bus         ,
    input ws_flush,
    //to ds
    output                         fs_to_ds_valid ,
    output [`ysyx_040991_FS_TO_DS_BUS_WD -1:0] fs_to_ds_bus   ,
    output [`ysyx_040991_EXCEPTION_WD-1:0] fs_to_ds_exception,
    // icache ports
    output                             icache_valid    ,
    output                             icache_uncache  ,
    output                             icache_op       , // 0: read only
    output [`ysyx_040991_CACHE_INDEX_WD    -1:0]   icache_index    , 
    output [`ysyx_040991_CACHE_TAG_WD      -1:0]   icache_tag      ,
    output [`ysyx_040991_CACHE_OFFSET_WD   -1:0]   icache_offset   ,

    input                              icache_addr_ok  ,
    input                              icache_data_ok  ,
    input                     [63:0]   icache_rdata    ,
    // from csr
    input  [`ysyx_040991_VADDR_WIDTH-1:0] csr_branch_pc
);
    reg fs_valid;
    wire fs_ready_go;
    wire fs_allowin;

    wire [`ysyx_040991_VADDR_WIDTH-1:0] snpc;
    wire [`ysyx_040991_VADDR_WIDTH-1:0] dnpc;
    wire br_taken;
    wire [`ysyx_040991_VADDR_WIDTH-1:0] br_target;
    wire br_stall;
    assign {br_stall, br_taken, br_target} = br_bus;

    wire    [`ysyx_040991_INST_WIDTH-1:0] fs_inst;
    reg     [`ysyx_040991_VADDR_WIDTH-1:0] fs_pc;

    assign fs_to_ds_bus = {
        fs_inst,
        fs_pc    
    };
    assign icache_op = 1'b0;
    wire [31:0] icache_raddr;
    assign {icache_tag, icache_index, icache_offset} = icache_raddr;
    assign icache_raddr = dnpc[31:0];
    assign icache_uncache = icache_raddr[31] ? 1'b0 : 1'b1;
    assign icache_valid = fs_allowin && !rst;

    wire pfs_ready_go;
    wire pfs_to_fs_valid;

    reg        ws_flush_r;
    reg [63:0] csr_branch_pc_r;

    reg        br_taken_r;
    reg [63:0] br_target_r;

    assign pfs_ready_go = icache_valid && icache_addr_ok;
    assign pfs_to_fs_valid = pfs_ready_go;

    always @(posedge clk) begin
        if(rst) begin
            br_taken_r <= 1'b0;
            br_target_r <= 64'b0;
        end
        else if(icache_addr_ok && fs_allowin)begin
            br_taken_r  <= 1'b0;
            br_target_r <= 64'b0;
        end
        else if(br_taken && !br_stall) begin
            br_taken_r <= 1'b1;
            br_target_r <= br_target;
        end

    end

    always @(posedge clk) begin
        if(rst) begin
            ws_flush_r <= 1'b0;
            csr_branch_pc_r <= 64'b0;
        end
        else if(ws_flush) begin
            ws_flush_r <= 1'b1;
            csr_branch_pc_r <= csr_branch_pc;
        end
        else if(icache_addr_ok && fs_allowin)begin
            ws_flush_r <= 1'b0;
            csr_branch_pc_r <= 64'b0;
        end
    end

    assign snpc =   fs_pc + 64'h4;
    assign dnpc =   ws_flush                ?   csr_branch_pc :
                    ws_flush_r              ?   csr_branch_pc_r :
                    br_taken_r              ?   br_target_r :
                    (br_taken && !br_stall) ?   br_target : 
                                                snpc;

    reg        fs_inst_valid;
    reg [63:0] fs_inst_buff;
    reg fs_inst_cancel;
    always @(posedge clk ) begin
        if(rst) begin
            fs_inst_cancel <= 1'b0;
        end
        else if(!fs_allowin && !fs_ready_go && ((ws_flush) ||( br_taken && ~br_stall))) begin
            fs_inst_cancel <= 1'b1;
        end
        else if(icache_data_ok) begin
            fs_inst_cancel <= 1'b0;
        end
    end

    // IF stage
    assign fs_ready_go = fs_inst_valid || (fs_valid && icache_data_ok);  
    assign fs_allowin = !fs_valid || fs_ready_go && ds_allowin;
    assign fs_to_ds_valid = fs_valid && fs_ready_go && !fs_inst_cancel && !(br_taken && !br_stall) && !(ws_flush || ws_flush_r); 
    
    always @(posedge clk) begin
        if (rst) begin
            fs_valid <= 1'b0;
        end
        else if (fs_allowin) begin
            fs_valid <= pfs_to_fs_valid;
        end

        if (rst) begin
            fs_pc <= 64'h2fff_fffc; 
        end
        else if (pfs_to_fs_valid && fs_allowin) begin
            fs_pc <= dnpc;
        end
    end

    always @(posedge clk ) begin
        if(rst) begin
            fs_inst_valid <= 1'b0;
        end
        else if(!fs_inst_valid && icache_data_ok && !ds_allowin) begin
            fs_inst_valid <= 1'b1;
        end
        else if (ds_allowin) begin
            fs_inst_valid <= 1'b0;
        end

        if (rst) begin
            fs_inst_buff <= 64'b0;
        end
        else if(!fs_inst_valid && icache_data_ok && !ds_allowin) begin
            fs_inst_buff <= icache_rdata;
        end
    end

    assign fs_inst = fs_inst_valid ? ((icache_offset[2] == 1'b1) ? fs_inst_buff[31:0] : fs_inst_buff[63:32]):
                        ((icache_offset[2] == 1'b1) ? icache_rdata[31:0] : icache_rdata[63:32]);

    // exception detect
    wire inst_addr_misaligned;
    assign inst_addr_misaligned = fs_pc[1:0] != 2'b00;

    wire fs_exception_en;
    assign fs_exception_en = inst_addr_misaligned;
    wire [5:0] fs_exception;

    assign fs_exception[5:4] = fs_exception_en ? `ysyx_040991_EXCEPTION_EXCEPTION : 2'b00;
    assign fs_exception[3:0] = {4{inst_addr_misaligned}} & `ysyx_040991_EXCEPTION_INST_ADDR_MISALIGNED;

    assign fs_to_ds_exception = fs_exception;
endmodule

module ysyx_040991_mem_stage(
    input                          clk           ,
    input                          rst         ,
    // allowin
    input                          ws_allowin    ,
    output                         ms_allowin    ,
    // from es
    input                          es_to_ms_valid,
    input  [`ysyx_040991_ES_TO_MS_BUS_WD -1:0] es_to_ms_bus  ,
    input  [`ysyx_040991_EXCEPTION_WD-1:0]      es_to_ms_exception,
    // to ws
    output                         ms_to_ws_valid,
    output [`ysyx_040991_MS_TO_WS_BUS_WD -1:0] ms_to_ws_bus  ,
    output [`ysyx_040991_EXCEPTION_WD      -1:0] ms_to_ws_exception,
    // from ctrl
    input ws_flush,
    output ms_is_exception,
    // dache ports
    input                                dcache_data_ok  ,
    input                       [63:0]   dcache_rdata,
    // forwarding bus
    output [`ysyx_040991_MS_FORWARDING_BUS_WD -1:0] ms_forwarding_bus,
    output [`ysyx_040991_CSR_FORWARDING_BUS_WD -1:0] ms_csr_forwarding_bus,
    // multiplier
    input  [`ysyx_040991_DATA_WIDTH-1:0] ms_mul_result,
    // divider
    input  [`ysyx_040991_DATA_WIDTH-1:0] ms_div_result
);

    reg         ms_valid;
    wire        ms_ready_go;

    reg  [`ysyx_040991_ES_TO_MS_BUS_WD -1:0] es_to_ms_bus_r;
    reg  [`ysyx_040991_EXCEPTION_WD      -1:0] es_to_ms_exception_r;
    wire [5:0] ms_wb_sel     ;
    wire [6:0] ms_load_op    ;
    wire ms_rd_wen     ;
    wire [4:0] ms_rd_addr    ;
    wire [`ysyx_040991_DATA_WIDTH-1:0] ms_alu_result ;
    wire [`ysyx_040991_DATA_WIDTH-1:0] ms_csr_rdata_final ;
    // wire [`ysyx_040991_DATA_WIDTH-1:0] ms_mul_result ;
    // wire [`ysyx_040991_DATA_WIDTH-1:0] ms_div_result ;
    wire [`ysyx_040991_VADDR_WIDTH-1:0] ms_pc         ;
    wire ms_csr_wen;
    wire [31:0] ms_inst;
    // wire ms_dcache_uncache;
    wire ms_data_ram_en;
    assign {
        // ms_dcache_uncache ,
        ms_data_ram_en  ,
        ms_wb_sel       ,
        ms_load_op      ,
        ms_rd_wen       ,
        ms_rd_addr      ,
        ms_alu_result   ,
        ms_csr_rdata_final,
        ms_csr_wen      ,
        ms_pc,
        ms_inst
    } = es_to_ms_bus_r;

    wire [`ysyx_040991_DATA_WIDTH-1:0] load_data;
    wire [`ysyx_040991_DATA_WIDTH-1:0] ms_final_result;

    assign ms_to_ws_bus = {
        // ms_dcache_uncache   ,
        ms_rd_wen           ,  
        ms_rd_addr          ,  
        ms_final_result     ,  
        ms_alu_result       ,
        ms_csr_wen          ,
        ms_pc               ,
        ms_inst             
    };

    wire [5:0] ms_exception_curr;
    assign ms_exception_curr = es_to_ms_exception_r;

    assign ms_is_exception = |ms_exception_curr[5:4] && ms_valid;
    reg ws_flush_r;
    always @(posedge clk) begin
        if (rst) begin
            ws_flush_r <= 1'b0;
        end else if (ws_flush) begin
            ws_flush_r <= 1'b1;
        end else if (es_to_ms_valid && ms_allowin) begin
            ws_flush_r <= 1'b0;
        end
    end
    assign ms_ready_go    = ms_data_ram_en ? dcache_data_ok || ms_is_exception || ws_flush || ws_flush_r: 1'b1;
    assign ms_allowin     = !ms_valid || ms_ready_go && ws_allowin;
    assign ms_to_ws_valid = ms_valid && ms_ready_go && !(ws_flush || ws_flush_r);
    always @(posedge clk) begin
        if (rst) begin
            ms_valid <= 1'b0;
        end
        else if (ms_allowin) begin
            ms_valid <= es_to_ms_valid;
        end

        if (es_to_ms_valid && ms_allowin) begin
            es_to_ms_bus_r <= es_to_ms_bus;
            es_to_ms_exception_r <= es_to_ms_exception;
        end
    end
    wire [`ysyx_040991_VADDR_WIDTH-1:0] data_ram_addr;
    assign data_ram_addr = ms_alu_result;
    assign load_data =  ({`ysyx_040991_DATA_WIDTH{ms_load_op[6]}} & (({`ysyx_040991_DATA_WIDTH{data_ram_addr[2:0]==3'b000}}  & {{56{dcache_rdata[ 7]}}, dcache_rdata[ 7: 0]}  )    // inst_lb
                                                        |({`ysyx_040991_DATA_WIDTH{data_ram_addr[2:0]==3'b001}}  & {{56{dcache_rdata[15]}}, dcache_rdata[15: 8]}  )
                                                        |({`ysyx_040991_DATA_WIDTH{data_ram_addr[2:0]==3'b010}}  & {{56{dcache_rdata[23]}}, dcache_rdata[23:16]}  )
                                                        |({`ysyx_040991_DATA_WIDTH{data_ram_addr[2:0]==3'b011}}  & {{56{dcache_rdata[31]}}, dcache_rdata[31:24]}  )
                                                        |({`ysyx_040991_DATA_WIDTH{data_ram_addr[2:0]==3'b100}}  & {{56{dcache_rdata[39]}}, dcache_rdata[39:32]}  )
                                                        |({`ysyx_040991_DATA_WIDTH{data_ram_addr[2:0]==3'b101}}  & {{56{dcache_rdata[47]}}, dcache_rdata[47:40]}  )
                                                        |({`ysyx_040991_DATA_WIDTH{data_ram_addr[2:0]==3'b110}}  & {{56{dcache_rdata[55]}}, dcache_rdata[55:48]}  )
                                                        |({`ysyx_040991_DATA_WIDTH{data_ram_addr[2:0]==3'b111}}  & {{56{dcache_rdata[63]}}, dcache_rdata[63:56]}  ))) 
                    // inst_lh
                    |   ({`ysyx_040991_DATA_WIDTH{ms_load_op[5]}} & (({`ysyx_040991_DATA_WIDTH{data_ram_addr[2:1]==2'b00 }}  & {{48{dcache_rdata[15]}}, dcache_rdata[15: 0]}  )   
                                                        |({`ysyx_040991_DATA_WIDTH{data_ram_addr[2:1]==2'b01 }}  & {{48{dcache_rdata[31]}}, dcache_rdata[31:16]}  )
                                                        |({`ysyx_040991_DATA_WIDTH{data_ram_addr[2:1]==2'b10 }}  & {{48{dcache_rdata[47]}}, dcache_rdata[47:32]}  )
                                                        |({`ysyx_040991_DATA_WIDTH{data_ram_addr[2:1]==2'b11 }}  & {{48{dcache_rdata[63]}}, dcache_rdata[63:48]}  )))
                    // inst_lw
                    |   ({`ysyx_040991_DATA_WIDTH{ms_load_op[4]}} & (({`ysyx_040991_DATA_WIDTH{data_ram_addr[2] == 1'b0  }}  & {{32{dcache_rdata[31]}}, dcache_rdata[31: 0]}  ) 
                                                        |({`ysyx_040991_DATA_WIDTH{data_ram_addr[2] == 1'b1  }}  & {{32{dcache_rdata[63]}}, dcache_rdata[63:32]}  )))
                    // inst_ld
                    |   ({`ysyx_040991_DATA_WIDTH{ms_load_op[3]}}  &   dcache_rdata)
                    // inst_lbu
                    |   ({`ysyx_040991_DATA_WIDTH{ms_load_op[2]}} & (({`ysyx_040991_DATA_WIDTH{data_ram_addr[2:0]==3'b000}}  & {56'b0, dcache_rdata[ 7: 0]}  )    
                                                        |({`ysyx_040991_DATA_WIDTH{data_ram_addr[2:0]==3'b001}}  & {56'b0, dcache_rdata[15: 8]}  )
                                                        |({`ysyx_040991_DATA_WIDTH{data_ram_addr[2:0]==3'b010}}  & {56'b0, dcache_rdata[23:16]}  )
                                                        |({`ysyx_040991_DATA_WIDTH{data_ram_addr[2:0]==3'b011}}  & {56'b0, dcache_rdata[31:24]}  )
                                                        |({`ysyx_040991_DATA_WIDTH{data_ram_addr[2:0]==3'b100}}  & {56'b0, dcache_rdata[39:32]}  )
                                                        |({`ysyx_040991_DATA_WIDTH{data_ram_addr[2:0]==3'b101}}  & {56'b0, dcache_rdata[47:40]}  )
                                                        |({`ysyx_040991_DATA_WIDTH{data_ram_addr[2:0]==3'b110}}  & {56'b0, dcache_rdata[55:48]}  )
                                                        |({`ysyx_040991_DATA_WIDTH{data_ram_addr[2:0]==3'b111}}  & {56'b0, dcache_rdata[63:56]}  )))
                    // inst_lhu
                    |   ({`ysyx_040991_DATA_WIDTH{ms_load_op[1]}} & (({`ysyx_040991_DATA_WIDTH{data_ram_addr[2:1]==2'b00 }}  & {48'b0, dcache_rdata[15: 0]}  )   
                                                        |({`ysyx_040991_DATA_WIDTH{data_ram_addr[2:1]==2'b01 }}  & {48'b0, dcache_rdata[31:16]}  )
                                                        |({`ysyx_040991_DATA_WIDTH{data_ram_addr[2:1]==2'b10 }}  & {48'b0, dcache_rdata[47:32]}  )
                                                        |({`ysyx_040991_DATA_WIDTH{data_ram_addr[2:1]==2'b11 }}  & {48'b0, dcache_rdata[63:48]}  )))
                    // inst_lwu
                    |   ({`ysyx_040991_DATA_WIDTH{ms_load_op[0]}} & (({`ysyx_040991_DATA_WIDTH{data_ram_addr[2] == 1'b0  }}  & {32'b0, dcache_rdata[31: 0]}  ) 
                                                        |({`ysyx_040991_DATA_WIDTH{data_ram_addr[2] == 1'b1  }}  & {32'b0, dcache_rdata[63:32]}  )));

    assign ms_final_result  =   ({`ysyx_040991_DATA_WIDTH{ms_wb_sel[0]}} & ms_csr_rdata_final) 
                            |   ({`ysyx_040991_DATA_WIDTH{ms_wb_sel[1]}} & ms_pc + 4) 
                            |   ({`ysyx_040991_DATA_WIDTH{ms_wb_sel[2]}} & ms_alu_result)
                            |   ({`ysyx_040991_DATA_WIDTH{ms_wb_sel[3]}} & load_data)
                            |   ({`ysyx_040991_DATA_WIDTH{ms_wb_sel[4]}} & ms_mul_result)
                            |   ({`ysyx_040991_DATA_WIDTH{ms_wb_sel[5]}} & ms_div_result);

    wire ms_ds_stall;
    assign ms_ds_stall = ms_data_ram_en && ms_valid && !ms_to_ws_valid;
    assign ms_forwarding_bus = {
        ms_ds_stall,
        ms_rd_wen && ms_valid && ms_to_ws_valid,// 
        ms_rd_addr,
        ms_final_result
    };
    assign ms_csr_forwarding_bus = {
        ms_csr_wen && ms_valid,
        ms_inst[31:20],
        ms_alu_result
    };
    // exception handling
    wire ms_exception_en;
    wire [5:0] ms_exception_next;
    assign ms_exception_en = |ms_exception_next[5:4];
    assign ms_exception_next[5:4] = 2'b0;
    assign ms_exception_next[3:0] = 4'b0;

    assign ms_to_ws_exception = ms_exception_en ? ms_exception_next : ms_exception_curr;


endmodule

module ysyx_040991_multiplier #(
    parameter ysyx_040991_DATA_WIDTH = 64
)(
    input clk,
    input rst,
    input [ysyx_040991_DATA_WIDTH-1:0] mul_src1,
    input [ysyx_040991_DATA_WIDTH-1:0] mul_src2,
    input [3:0] mul_op,
    input word_op, // word instructions 
    output [ysyx_040991_DATA_WIDTH-1:0] mul_wb_result
);

    wire op_mul     = mul_op[ 3] & ~word_op;
    wire op_mulh    = mul_op[ 2] & ~word_op;
    wire op_mulhsu  = mul_op[ 1] & ~word_op;
    wire op_mulhu   = mul_op[ 0] & ~word_op;
    wire op_mulw    = mul_op[ 3] &  word_op;

    reg op_mul_r    ;
    reg op_mulh_r   ;
    reg op_mulhsu_r ;
    reg op_mulhu_r  ;
    reg op_mulw_r   ;
    always @ (posedge clk) begin
        if(rst) begin
            op_mul_r    <= 1'b0;
            op_mulh_r   <= 1'b0;
            op_mulhsu_r <= 1'b0;
            op_mulhu_r  <= 1'b0;
            op_mulw_r   <= 1'b0;
        end
        else begin
            op_mul_r    <= op_mul   ;
            op_mulh_r   <= op_mulh  ;
            op_mulhsu_r <= op_mulhsu;
            op_mulhu_r  <= op_mulhu ;
            op_mulw_r   <= op_mulw  ;
        end
    end
    // radix-4 booth multiplier
    wire [2*ysyx_040991_DATA_WIDTH+1:0] mul_x;
    wire [2*ysyx_040991_DATA_WIDTH+1:0] mul_2x;
    wire [2*ysyx_040991_DATA_WIDTH+1:0] mul_3x;
    wire [2*ysyx_040991_DATA_WIDTH+1:0] mul_4x;
    wire [2*ysyx_040991_DATA_WIDTH+1:0] mul_5x;
    wire [2*ysyx_040991_DATA_WIDTH+1:0] mul_6x;
    wire [2*ysyx_040991_DATA_WIDTH+1:0] mul_7x;
    wire [2*ysyx_040991_DATA_WIDTH+1:0] mul_8x;
    
    wire [2*ysyx_040991_DATA_WIDTH+1:0] mul_nx;
    wire [2*ysyx_040991_DATA_WIDTH+1:0] mul_n2x;
    wire [2*ysyx_040991_DATA_WIDTH+1:0] mul_n3x;
    wire [2*ysyx_040991_DATA_WIDTH+1:0] mul_n4x;
    wire [2*ysyx_040991_DATA_WIDTH+1:0] mul_n5x;
    wire [2*ysyx_040991_DATA_WIDTH+1:0] mul_n6x;
    wire [2*ysyx_040991_DATA_WIDTH+1:0] mul_n7x;
    wire [2*ysyx_040991_DATA_WIDTH+1:0] mul_n8x;

    assign mul_x  = ({130{op_mul | op_mulh | op_mulhsu | op_mulw}} & {{66{mul_src1[63]}}, mul_src1}) // signed extend multiplier
                |   ({130{op_mulhu}} & {66'b0, mul_src1}); // zero extend multiplier
    assign mul_2x = mul_x << 1;
    assign mul_3x = mul_x + mul_2x;
    assign mul_4x = mul_x << 2;
    assign mul_5x = mul_x + mul_4x;
    assign mul_6x = mul_2x + mul_4x;
    assign mul_7x = mul_x + mul_2x + mul_4x;
    assign mul_8x = mul_x << 3;

    assign mul_nx  = ~mul_x  + 1;
    assign mul_n2x = ~mul_2x + 1;
    assign mul_n3x = ~mul_3x + 1;
    assign mul_n4x = ~mul_4x + 1;
    assign mul_n5x = ~mul_5x + 1;
    assign mul_n6x = ~mul_6x + 1;
    assign mul_n7x = ~mul_7x + 1;
    assign mul_n8x = ~mul_8x + 1;

    // decode
    wire [ysyx_040991_DATA_WIDTH+4:0] mul_y_ext; // 69bit extended multiplier
    assign mul_y_ext =  ({69{op_mul | op_mulh | op_mulw}} & {{4{mul_src2[63]}}, mul_src2, 1'b0})
                    |   ({69{op_mulhsu | op_mulhu}} & {4'b0, mul_src2, 1'b0});

    wire [4:0] mul_y_bits [0:16];
    wire [129:0] partial_product [0:16]; // bus [i*2*ysyx_040991_DATA_WIDTH+:2*ysyx_040991_DATA_WIDTH]
    
    genvar i;
    generate
        for (i=0; i<17; i=i+1) begin
            assign mul_y_bits[i] = mul_y_ext[4*i+:5];
            assign partial_product[i] = (({130{mul_y_bits[i] == 5'b00000 || mul_y_bits[i] == 5'b11111}} & 130'b0)
                                    |   ({130{mul_y_bits[i] == 5'b00001 || mul_y_bits[i] == 5'b00010}} & mul_x)
                                    |   ({130{mul_y_bits[i] == 5'b00011 || mul_y_bits[i] == 5'b00100}} & mul_2x)
                                    |   ({130{mul_y_bits[i] == 5'b00101 || mul_y_bits[i] == 5'b00110}} & mul_3x)
                                    |   ({130{mul_y_bits[i] == 5'b00111 || mul_y_bits[i] == 5'b01000}} & mul_4x)
                                    |   ({130{mul_y_bits[i] == 5'b01001 || mul_y_bits[i] == 5'b01010}} & mul_5x)
                                    |   ({130{mul_y_bits[i] == 5'b01011 || mul_y_bits[i] == 5'b01100}} & mul_6x)
                                    |   ({130{mul_y_bits[i] == 5'b01101 || mul_y_bits[i] == 5'b01110}} & mul_7x)
                                    |   ({130{mul_y_bits[i] == 5'b01111}} & mul_8x)
                                    |   ({130{mul_y_bits[i] == 5'b10000}} & mul_n8x)
                                    |   ({130{mul_y_bits[i] == 5'b10001 || mul_y_bits[i] == 5'b10010}} & mul_n7x)
                                    |   ({130{mul_y_bits[i] == 5'b10011 || mul_y_bits[i] == 5'b10100}} & mul_n6x)
                                    |   ({130{mul_y_bits[i] == 5'b10101 || mul_y_bits[i] == 5'b10110}} & mul_n5x)
                                    |   ({130{mul_y_bits[i] == 5'b10111 || mul_y_bits[i] == 5'b11000}} & mul_n4x)
                                    |   ({130{mul_y_bits[i] == 5'b11001 || mul_y_bits[i] == 5'b11010}} & mul_n3x)
                                    |   ({130{mul_y_bits[i] == 5'b11011 || mul_y_bits[i] == 5'b11100}} & mul_n2x)
                                    |   ({130{mul_y_bits[i] == 5'b11101 || mul_y_bits[i] == 5'b11110}} & mul_nx)) << (4*i);
        end
    endgenerate
    
    // pipeline
    reg [129:0] partial_product_r [0:16];
    integer m;
    always@(posedge clk) begin
        if (rst) begin
            for (m=0; m<17; m=m+1) begin
                partial_product_r[m] <= 130'b0;
            end
        end
        else begin
            for (m=0; m<17; m=m+1) begin
                partial_product_r[m] <= partial_product[m];
            end
        end
    end
                
    wire [16:0] partial_product_switch [0:129];
    genvar j;
    generate
        for (j=0; j<130; j=j+1) begin 
            for (i=0; i<17; i=i+1) begin                      
                assign partial_product_switch[j][i] = partial_product_r[i][j];
            end
        end
    endgenerate
    wire [13:0] cout_bus [0:130] /*verilator split_var*/; // cout_bus[2*ysyx_040991_DATA_WIDTH] unused
    assign cout_bus[0] = 14'b0;
    wire [129:0] c_final_bus;
    wire [129:0] s_final_bus;
    genvar k;
    generate
        for (k=0; k<130; k=k+1) begin: wtrees
            ysyx_040991_wallace_tree u_wtree(
                .din        (partial_product_switch[k]  ),
                .cin        (cout_bus[k]                ),
                .cout       (cout_bus[k+1]              ),
                .c_final    (c_final_bus[k]             ),
                .s_final    (s_final_bus[k]             )
            );
        end
    endgenerate

    wire [130:0] c_s_sum;
    wire [127:0] mul_product;
    assign c_s_sum = {c_final_bus[128:0], 1'b0} + s_final_bus;
    assign mul_product = c_s_sum[127:0];
    wire [63:0] mul_result    = mul_product[63:0];
    wire [63:0] mulh_result   = mul_product[127:64];
    wire [63:0] mulw_result   = {{32{mul_product[31]}}, mul_product[31:0]};

    assign mul_wb_result    = ({64{op_mul_r     }}  &   mul_result)
                            | ({64{op_mulh_r    }}  &   mulh_result)
                            | ({64{op_mulhsu_r  }}  &   mulh_result)
                            | ({64{op_mulhu_r   }}  &   mulh_result)
                            | ({64{op_mulw_r    }}  &   mulw_result);

endmodule

module ysyx_040991_npc_core (
    input clk,
    input rst,
    input interrupt_taken,

    // to cache
    output                               icache_valid    ,
    output                               icache_uncache  ,
    output                               icache_op       , // 0: read only
    output   [`ysyx_040991_CACHE_INDEX_WD    -1:0]   icache_index    , 
    output   [`ysyx_040991_CACHE_TAG_WD      -1:0]   icache_tag      ,
    output   [`ysyx_040991_CACHE_OFFSET_WD   -1:0]   icache_offset   ,

    input                                icache_addr_ok  ,
    input                                icache_data_ok  ,
    input                       [63:0]   icache_rdata    ,

    // ports of dcache
    output                               dcache_valid    ,
    output                               dcache_uncache  ,
    output                               dcache_op       , // 0: read, 1: write
    output   [`ysyx_040991_CACHE_INDEX_WD    -1:0]   dcache_index    , 
    output   [`ysyx_040991_CACHE_TAG_WD      -1:0]   dcache_tag      ,
    output   [`ysyx_040991_CACHE_OFFSET_WD   -1:0]   dcache_offset   ,
    output                      [ 7:0]   dcache_wstrb    ,
    output                      [63:0]   dcache_wdata    ,

    input                                dcache_addr_ok  ,
    input                                dcache_data_ok  ,
    input                       [63:0]   dcache_rdata    ,
    
    output                               es_fence,
    input                                es_fence_complete
);
    /* verilator lint_off UNOPTFLAT */
    // IF stage
    wire                            ds_allowin;
    wire [`ysyx_040991_BR_BUS_WD          :0]   br_bus;
    wire                            fs_to_ds_valid;
    wire [`ysyx_040991_FS_TO_DS_BUS_WD  -1:0]   fs_to_ds_bus;
    wire [`ysyx_040991_EXCEPTION_WD     -1:0]   fs_to_ds_exception;
    wire [`ysyx_040991_VADDR_WIDTH      -1:0]   csr_branch_pc;
    wire ws_is_eret;
    wire ws_is_exception;
    wire ms_is_exception;
    wire ws_flush;
    assign ws_flush = ws_is_eret || ws_is_exception;
    ysyx_040991_if_stage u_fs (
        .clk                (clk                ),
        .rst                (rst                ),
        // allowin
        .ds_allowin         (ds_allowin         ),
        // br_bus
        .br_bus             (br_bus             ),
        .ws_flush           (ws_flush           ),
        // to ds
        .fs_to_ds_valid     (fs_to_ds_valid     ),
        .fs_to_ds_bus       (fs_to_ds_bus       ),
        .fs_to_ds_exception (fs_to_ds_exception ),
        // icache ports
        .icache_valid       (icache_valid       ),
        .icache_uncache     (icache_uncache     ),
        .icache_op          (icache_op          ),
        .icache_index       (icache_index       ),
        .icache_tag         (icache_tag         ),
        .icache_offset      (icache_offset      ),
        .icache_addr_ok     (icache_addr_ok     ),
        .icache_data_ok     (icache_data_ok     ),
        .icache_rdata       (icache_rdata       ),
        // from csr
        .csr_branch_pc      (csr_branch_pc      )
    );

    // id_stage
    wire                                es_allowin;
    wire                                ds_to_es_valid;
    wire [`ysyx_040991_DS_TO_ES_BUS_WD      -1:0]   ds_to_es_bus;
    wire [`ysyx_040991_EXCEPTION_WD         -1:0]   ds_to_es_exception;
    wire [`ysyx_040991_WS_TO_RF_BUS_WD      -1:0]   ws_to_rf_bus;
    wire [`ysyx_040991_ES_FORWARDING_BUS_WD -1:0]   es_forwarding_bus;
    wire [`ysyx_040991_MS_FORWARDING_BUS_WD -1:0]   ms_forwarding_bus;
    wire [`ysyx_040991_WS_FORWARDING_BUS_WD -1:0]   ws_forwarding_bus;
    wire [`ysyx_040991_BR_BUS_WD              :0]   ds_branch_bus;
    wire                                timer_interrupt_en;
    ysyx_040991_id_stage u_ds (
        .clk(clk),
        .rst(rst),
        // interrupt
        .interrupt_taken(timer_interrupt_en),
        // allowin
        .es_allowin(es_allowin),
        .ds_allowin(ds_allowin),
        // from fs
        .fs_to_ds_valid(fs_to_ds_valid),
        .fs_to_ds_bus(fs_to_ds_bus),
        .fs_to_ds_exception(fs_to_ds_exception),
        // to es
        .ds_to_es_valid(ds_to_es_valid),
        .ds_to_es_bus(ds_to_es_bus),
        .ds_to_es_exception(ds_to_es_exception),
        // to from ctrl
        .ds_branch_bus(ds_branch_bus),
        .ws_flush           (ws_flush           ),
        // to rf: from wb stage
        .ws_to_rf_bus(ws_to_rf_bus),
        // forwarding bus
        .es_forwarding_bus(es_forwarding_bus),
        .ms_forwarding_bus(ms_forwarding_bus),
        .ws_forwarding_bus(ws_forwarding_bus)
    );
    
    // exe_stage
    wire                            ms_allowin;
    wire                            es_to_ms_valid;
    wire [`ysyx_040991_ES_TO_MS_BUS_WD  -1:0]   es_to_ms_bus;
    wire [`ysyx_040991_EXCEPTION_WD     -1:0]   es_to_ms_exception;
    wire ds_to_es_hold;
    wire [`ysyx_040991_BR_BUS_WD-1:0] es_branch_bus;
    wire [`ysyx_040991_DATA_WIDTH-1:0] es_alu_src1;
    wire [`ysyx_040991_DATA_WIDTH-1:0] es_alu_src2;
    wire [ 3:0] es_mul_op;
    wire [ 3:0] es_div_op;
    wire es_word_op;
    wire div;
    // wire div_complete;
    wire [11:0] es_csr_raddr;
    wire [63:0] es_csr_rdata;
    wire [`ysyx_040991_CSR_FORWARDING_BUS_WD -1:0] ms_csr_forwarding_bus;
    wire [`ysyx_040991_CSR_FORWARDING_BUS_WD -1:0] ws_csr_forwarding_bus;
    ysyx_040991_exe_stage u_es (
        .clk                   (clk                   ),
        .rst                   (rst                   ),
        // allowin
        .ms_allowin            (ms_allowin            ),
        .es_allowin            (es_allowin            ),
        // from ds
        .ds_to_es_valid        (ds_to_es_valid        ),
        .ds_to_es_bus          (ds_to_es_bus          ),
        .ds_to_es_exception    (ds_to_es_exception    ),
        // to ms
        .es_to_ms_valid        (es_to_ms_valid        ),
        .es_to_ms_bus          (es_to_ms_bus          ),
        .es_to_ms_exception    (es_to_ms_exception    ),
        // to from ctrl
        .ds_to_es_hold         (ds_to_es_hold         ),
        .es_branch_bus         (es_branch_bus         ),
        .ws_flush              (ws_flush              ),
        .ms_is_exception       (ms_is_exception       ),
        // dcache ports
        .dcache_valid          (dcache_valid          ),
        .dcache_uncache        (dcache_uncache        ),
        .dcache_op             (dcache_op             ),
        .dcache_index          (dcache_index          ),
        .dcache_tag            (dcache_tag            ),
        .dcache_offset         (dcache_offset         ),
        .dcache_wstrb          (dcache_wstrb          ),
        .dcache_wdata          (dcache_wdata          ),
        .dcache_addr_ok        (dcache_addr_ok        ),
        .es_fence              (es_fence              ),
        .es_fence_complete     (es_fence_complete     ),
        // forwarding bus
        .es_forwarding_bus     (es_forwarding_bus     ),
        // multiplier divider
        .es_alu_src1           (es_alu_src1           ),
        .es_alu_src2           (es_alu_src2           ),
        .es_mul_op             (es_mul_op             ),
        .es_div_op             (es_div_op             ),
        .es_word_op            (es_word_op            ),
        .div                   (div                   ),
        // csr
        .es_csr_raddr          (es_csr_raddr          ),
        .es_csr_rdata          (es_csr_rdata          ),
        // forwarding csr register
        .ms_csr_forwarding_bus (ms_csr_forwarding_bus ),
        .ws_csr_forwarding_bus (ws_csr_forwarding_bus )

    );

    // mem_stage
    wire                                ws_allowin;
    wire                                ms_to_ws_valid;
    wire [`ysyx_040991_MS_TO_WS_BUS_WD      -1:0]   ms_to_ws_bus;
    wire [`ysyx_040991_DATA_WIDTH-1:0] ms_mul_result;
    wire [`ysyx_040991_DATA_WIDTH-1:0] ms_div_result;
    wire [`ysyx_040991_EXCEPTION_WD         -1:0]   ms_to_ws_exception;
    ysyx_040991_mem_stage u_ms (
        .clk                   (clk                   ),
        .rst                   (rst                   ),
        // allowin 
        .ws_allowin            (ws_allowin            ),
        .ms_allowin            (ms_allowin            ),
        // from es
        .es_to_ms_valid        (es_to_ms_valid        ),
        .es_to_ms_bus          (es_to_ms_bus          ),
        .es_to_ms_exception    (es_to_ms_exception    ),
        // to ws
        .ms_to_ws_valid        (ms_to_ws_valid        ),
        .ms_to_ws_bus          (ms_to_ws_bus          ),
        .ms_to_ws_exception    (ms_to_ws_exception    ),
        // from ctrl
        .ws_flush              (ws_flush              ),
        .ms_is_exception       (ms_is_exception       ),
        // dcache ports
        .dcache_data_ok        (dcache_data_ok        ),
        .dcache_rdata          (dcache_rdata          ),
        // forwarding bus
        .ms_forwarding_bus     (ms_forwarding_bus     ),
        .ms_csr_forwarding_bus (ms_csr_forwarding_bus ),
        // multiplier divider results
        .ms_mul_result         (ms_mul_result         ),
        .ms_div_result         (ms_div_result         )
    );

    wire csr_wen;
    wire [11:0] csr_waddr;
    wire [63:0] csr_wdata;
    wire [5:0] ws_exception;
    wire ws_exception_valid;
    wire [63:0]  ws_exception_pc;
    // wb_stage
    ysyx_040991_wb_stage u_ws(
        .clk(clk),
        .rst(rst),
        // allowin
        .ws_allowin(ws_allowin),
        // from ms
        .ms_to_ws_valid(ms_to_ws_valid),
        .ms_to_ws_bus(ms_to_ws_bus),
        .ms_to_ws_exception(ms_to_ws_exception),
        // to rf: for write back
        .ws_to_rf_bus(ws_to_rf_bus),
        // forwarding bus
        .ws_forwarding_bus(ws_forwarding_bus),
        .ws_csr_forwarding_bus(ws_csr_forwarding_bus),

        // to csr
        .csr_wen               (csr_wen               ),
        .csr_waddr             (csr_waddr             ),
        .csr_wdata             (csr_wdata             ),
        .ws_exception          (ws_exception          ),
        .ws_is_exception       (ws_is_exception       ),
        .ws_is_eret            (ws_is_eret            ),
        .ws_exception_valid    (ws_exception_valid    ),
        .ws_exception_pc       (ws_exception_pc       )
    );

    // stall
    // stall fs_to_ds_bus and ds_to_es_bus
    
    ysyx_040991_ctrl u_ctrl(
        // from ds
        .ds_branch_bus(ds_branch_bus),
        // from es
        .es_branch_bus(es_branch_bus),
        // to es
        .ds_to_es_hold(ds_to_es_hold),
        // to fs
        .br_bus(br_bus)
    );

    // exe stage -> mul, div -> mem_stage
    ysyx_040991_multiplier #(
        .ysyx_040991_DATA_WIDTH(`ysyx_040991_DATA_WIDTH)
    ) u_multiplier (
        .clk(clk),
        .rst(rst),
        .mul_src1(es_alu_src1),
        .mul_src2(es_alu_src2),
        .mul_op(es_mul_op),
        .word_op(es_word_op),
        .mul_wb_result(ms_mul_result)
    );
 
    ysyx_040991_divider #(
        .ysyx_040991_DATA_WIDTH(`ysyx_040991_DATA_WIDTH)
    ) u_divider (
        .clk(clk),
        .rst(rst),
        .div_src1(es_alu_src1),
        .div_src2(es_alu_src2),
        .div_op(es_div_op),
        .word_op(es_word_op),
        .div(div),
        // .complete(div_complete),
        .div_wb_result(ms_div_result)
    );
    
    ysyx_040991_csr u_csr(
    	.clk               (clk               ),
        .rst               (rst               ),
        .timer_intr        (interrupt_taken   ),
        .csr_raddr         (es_csr_raddr      ),
        .csr_rdata         (es_csr_rdata      ),
        .csr_wen           (csr_wen           ),
        .csr_waddr         (csr_waddr         ),
        .csr_wdata         (csr_wdata         ),
        .ws_exception      (ws_exception      ),
        .ws_exception_valid(ws_exception_valid),
        .ws_exception_pc   (ws_exception_pc   ),
        .csr_branch_pc     (csr_branch_pc     ),
        .timer_interrupt_en(timer_interrupt_en)
    );
    
endmodule

module ysyx_040991_npc_top #(
    parameter C_M_AXI_ID_WIDTH	    = 4,
    parameter C_M_AXI_ADDR_WIDTH	= 32,
    parameter C_M_AXI_ysyx_040991_DATA_WIDTH	= 64,
    parameter C_M_AXI_AWUSER_WIDTH	= 1,
    parameter C_M_AXI_ARUSER_WIDTH	= 1,
    parameter C_M_AXI_WUSER_WIDTH	= 1,
    parameter C_M_AXI_RUSER_WIDTH	= 1,
    parameter C_M_AXI_BUSER_WIDTH	= 1,
    parameter C_S_AXI_ID_WIDTH	    = 4,
    parameter C_S_AXI_ysyx_040991_DATA_WIDTH	= 64,
    parameter C_S_AXI_ADDR_WIDTH	= 32,
    parameter C_S_AXI_AWUSER_WIDTH	= 1,
    parameter C_S_AXI_ARUSER_WIDTH	= 1,
    parameter C_S_AXI_WUSER_WIDTH	= 1,
    parameter C_S_AXI_RUSER_WIDTH	= 1,
    parameter C_S_AXI_BUSER_WIDTH	= 1
    )(
    input clk,
    input rst,
    input interrupt_taken,

    // M_AXI4
    output wire [C_M_AXI_ID_WIDTH-1 : 0]        mem_axi_awid,
    output wire [C_M_AXI_ADDR_WIDTH-1 : 0]      mem_axi_awaddr,
    output wire [7 : 0]                         mem_axi_awlen,
    output wire [2 : 0]                         mem_axi_awsize,
    output wire [1 : 0]                         mem_axi_awburst,
    output wire                                 mem_axi_awlock,
    output wire [3 : 0]                         mem_axi_awcache,
    output wire [2 : 0]                         mem_axi_awprot,
    output wire [3 : 0]                         mem_axi_awqos,
    output wire [3 : 0]                         mem_axi_awregion,           
    output wire [C_M_AXI_AWUSER_WIDTH-1 : 0]    mem_axi_awuser,
    output wire                                 mem_axi_awvalid,
    input wire                                  mem_axi_awready,
    output wire [C_M_AXI_ysyx_040991_DATA_WIDTH-1 : 0]      mem_axi_wdata,
    output wire [C_M_AXI_ysyx_040991_DATA_WIDTH/8-1 : 0]    mem_axi_wstrb,
    output wire                                 mem_axi_wlast,
    output wire [C_M_AXI_WUSER_WIDTH-1 : 0]     mem_axi_wuser,
    output wire                                 mem_axi_wvalid,
    input wire                                  mem_axi_wready,
    input wire [C_M_AXI_ID_WIDTH-1 : 0]         mem_axi_bid,
    input wire [1 : 0]                          mem_axi_bresp,
    input wire [C_M_AXI_BUSER_WIDTH-1 : 0]      mem_axi_buser,
    input wire                                  mem_axi_bvalid,
    output wire                                 mem_axi_bready,
    output wire [C_M_AXI_ID_WIDTH-1 : 0]        mem_axi_arid,
    output wire [C_M_AXI_ADDR_WIDTH-1 : 0]      mem_axi_araddr,
    output wire [7 : 0]                         mem_axi_arlen,
    output wire [2 : 0]                         mem_axi_arsize,
    output wire [1 : 0]                         mem_axi_arburst,
    output wire                                 mem_axi_arlock,
    output wire [3 : 0]                         mem_axi_arcache,
    output wire [2 : 0]                         mem_axi_arprot,
    output wire [3 : 0]                         mem_axi_arqos,
    output wire [3 : 0]                         mem_axi_arregion,           
    output wire [C_M_AXI_ARUSER_WIDTH-1 : 0]    mem_axi_aruser,
    output wire                                 mem_axi_arvalid,
    input wire                                  mem_axi_arready,
    input wire [C_M_AXI_ID_WIDTH-1 : 0]         mem_axi_rid,
    input wire [C_M_AXI_ysyx_040991_DATA_WIDTH-1 : 0]       mem_axi_rdata,
    input wire [1 : 0]                          mem_axi_rresp,
    input wire                                  mem_axi_rlast,
    input wire [C_M_AXI_RUSER_WIDTH-1 : 0]      mem_axi_ruser,
    input wire                                  mem_axi_rvalid,
    output wire                                 mem_axi_rready,

    // S_AXI4
    input wire [C_S_AXI_ID_WIDTH-1 : 0]         io_slave_awid,
    input wire [C_S_AXI_ADDR_WIDTH-1 : 0]       io_slave_awaddr,
    input wire [7 : 0]                          io_slave_awlen,
    input wire [2 : 0]                          io_slave_awsize,
    input wire [1 : 0]                          io_slave_awburst,
    input wire                                  io_slave_awlock,
    input wire [3 : 0]                          io_slave_awcache,
    input wire [2 : 0]                          io_slave_awprot,
    input wire [3 : 0]                          io_slave_awqos,
    input wire [3 : 0]                          io_slave_awregion,
    input wire [C_S_AXI_AWUSER_WIDTH-1 : 0]     io_slave_awuser,
    input wire                                  io_slave_awvalid,
    output wire                                 io_slave_awready,
    input wire [C_S_AXI_ysyx_040991_DATA_WIDTH-1 : 0]       io_slave_wdata,
    input wire [(C_S_AXI_ysyx_040991_DATA_WIDTH/8)-1 : 0]   io_slave_wstrb,
    input wire                                  io_slave_wlast,
    input wire [C_S_AXI_WUSER_WIDTH-1 : 0]      io_slave_wuser,
    input wire                                  io_slave_wvalid,
    output wire                                 io_slave_wready,
    output wire [C_S_AXI_ID_WIDTH-1 : 0]        io_slave_bid,
    output wire [1 : 0]                         io_slave_bresp,
    output wire [C_S_AXI_BUSER_WIDTH-1 : 0]     io_slave_buser,
    output wire                                 io_slave_bvalid,
    input wire                                  io_slave_bready,
    input wire [C_S_AXI_ID_WIDTH-1 : 0]         io_slave_arid,
    input wire [C_S_AXI_ADDR_WIDTH-1 : 0]       io_slave_araddr,
    input wire [7 : 0]                          io_slave_arlen,
    input wire [2 : 0]                          io_slave_arsize,
    input wire [1 : 0]                          io_slave_arburst,
    input wire                                  io_slave_arlock,
    input wire [3 : 0]                          io_slave_arcache,
    input wire [2 : 0]                          io_slave_arprot,
    input wire [3 : 0]                          io_slave_arqos,
    input wire [3 : 0]                          io_slave_arregion,
    input wire [C_S_AXI_ARUSER_WIDTH-1 : 0]     io_slave_aruser,
    input wire                                  io_slave_arvalid,
    output wire                                 io_slave_arready,
    output wire [C_S_AXI_ID_WIDTH-1 : 0]        io_slave_rid,
    output wire [C_S_AXI_ysyx_040991_DATA_WIDTH-1 : 0]      io_slave_rdata,
    output wire [1 : 0]                         io_slave_rresp,
    output wire                                 io_slave_rlast,
    output wire [C_S_AXI_RUSER_WIDTH-1 : 0]     io_slave_ruser,
    output wire                                 io_slave_rvalid,
    input wire                                  io_slave_rready,

    // icache
    // data_bank0 of ram0
    input  [128 -1:0]   icache_ram0_bank0_Q,
    output              icache_ram0_bank0_CEN,
    output              icache_ram0_bank0_WEN,
    output [128 -1:0]   icache_ram0_bank0_BWEN,
    output [6   -1:0]   icache_ram0_bank0_A,
    output [128 -1:0]   icache_ram0_bank0_D,
    // data_bank1 of ram0
    input  [128 -1:0]   icache_ram0_bank1_Q,
    output              icache_ram0_bank1_CEN,
    output              icache_ram0_bank1_WEN,
    output [128 -1:0]   icache_ram0_bank1_BWEN,
    output [6   -1:0]   icache_ram0_bank1_A,
    output [128 -1:0]   icache_ram0_bank1_D,
    // data_bank0 of ram1
    input  [128 -1:0]   icache_ram1_bank0_Q,
    output              icache_ram1_bank0_CEN,
    output              icache_ram1_bank0_WEN,
    output [128 -1:0]   icache_ram1_bank0_BWEN,
    output [6   -1:0]   icache_ram1_bank0_A,
    output [128 -1:0]   icache_ram1_bank0_D,
    // data_bank1 of ram1
    input  [128 -1:0]   icache_ram1_bank1_Q,
    output              icache_ram1_bank1_CEN,
    output              icache_ram1_bank1_WEN,
    output [128 -1:0]   icache_ram1_bank1_BWEN,
    output [6   -1:0]   icache_ram1_bank1_A,
    output [128 -1:0]   icache_ram1_bank1_D,

    // dcache
    // data_bank0 of ram0
    input  [128 -1:0]   dcache_ram0_bank0_Q,
    output              dcache_ram0_bank0_CEN,
    output              dcache_ram0_bank0_WEN,
    output [128 -1:0]   dcache_ram0_bank0_BWEN,
    output [6   -1:0]   dcache_ram0_bank0_A,
    output [128 -1:0]   dcache_ram0_bank0_D,
    // data_bank1 of ram0
    input  [128 -1:0]   dcache_ram0_bank1_Q,
    output              dcache_ram0_bank1_CEN,
    output              dcache_ram0_bank1_WEN,
    output [128 -1:0]   dcache_ram0_bank1_BWEN,
    output [6   -1:0]   dcache_ram0_bank1_A,
    output [128 -1:0]   dcache_ram0_bank1_D,
    // data_bank0 of ram1
    input  [128 -1:0]   dcache_ram1_bank0_Q,
    output              dcache_ram1_bank0_CEN,
    output              dcache_ram1_bank0_WEN,
    output [128 -1:0]   dcache_ram1_bank0_BWEN,
    output [6   -1:0]   dcache_ram1_bank0_A,
    output [128 -1:0]   dcache_ram1_bank0_D,
    // data_bank1 of ram1
    input  [128 -1:0]   dcache_ram1_bank1_Q,
    output              dcache_ram1_bank1_CEN,
    output              dcache_ram1_bank1_WEN,
    output [128 -1:0]   dcache_ram1_bank1_BWEN,
    output [6   -1:0]   dcache_ram1_bank1_A,
    output [128 -1:0]   dcache_ram1_bank1_D
    );

    wire                               icache_valid    ;
    wire                               icache_uncache  ;
    wire                               icache_op       ; // 0: read only
    wire   [`ysyx_040991_CACHE_INDEX_WD    -1:0]   icache_index    ; 
    wire   [`ysyx_040991_CACHE_TAG_WD      -1:0]   icache_tag      ;
    wire   [`ysyx_040991_CACHE_OFFSET_WD   -1:0]   icache_offset   ;
    wire                               icache_addr_ok  ;
    wire                               icache_data_ok  ;
    wire                    [63:0]     icache_rdata    ;

    wire                               dcache_valid    ;
    wire                               dcache_uncache  ;
    wire                               dcache_op       ; // 0: read, 1: write
    wire   [`ysyx_040991_CACHE_INDEX_WD    -1:0]   dcache_index    ; 
    wire   [`ysyx_040991_CACHE_TAG_WD      -1:0]   dcache_tag      ;
    wire   [`ysyx_040991_CACHE_OFFSET_WD   -1:0]   dcache_offset   ;
    wire                      [ 7:0]   dcache_wstrb    ;
    wire                      [63:0]   dcache_wdata    ;

    wire                               dcache_addr_ok  ;
    wire                               dcache_data_ok  ;
    wire                    [63:0]     dcache_rdata    ;


    wire es_fence;
    wire es_fence_complete;

    ysyx_040991_npc_core u_npc_core(
    	.clk             (clk             ),
        .rst             (rst             ),
        .interrupt_taken (interrupt_taken ),

        // to cache
        .icache_valid    (icache_valid    ),
        .icache_uncache  (icache_uncache  ),
        .icache_op       (icache_op       ),
        .icache_index    (icache_index    ),
        .icache_tag      (icache_tag      ),
        .icache_offset   (icache_offset   ),
        .icache_addr_ok  (icache_addr_ok  ),
        .icache_data_ok  (icache_data_ok  ),
        .icache_rdata    (icache_rdata    ),
        .dcache_valid    (dcache_valid    ),
        .dcache_uncache  (dcache_uncache  ),
        .dcache_op       (dcache_op       ),
        .dcache_index    (dcache_index    ),
        .dcache_tag      (dcache_tag      ),
        .dcache_offset   (dcache_offset   ),
        .dcache_wstrb    (dcache_wstrb    ),
        .dcache_wdata    (dcache_wdata    ),
        .dcache_addr_ok  (dcache_addr_ok  ),
        .dcache_data_ok  (dcache_data_ok  ),
        .dcache_rdata    (dcache_rdata    ),
        .es_fence           (es_fence       ),
        .es_fence_complete  (es_fence_complete)
    );
    

    ysyx_040991_cache_top 
    #(
        .C_M_AXI_ID_WIDTH           (C_M_AXI_ID_WIDTH           ),
        .C_M_AXI_ADDR_WIDTH         (C_M_AXI_ADDR_WIDTH         ),
        .C_M_AXI_ysyx_040991_DATA_WIDTH         (C_M_AXI_ysyx_040991_DATA_WIDTH         ),
        .C_M_AXI_AWUSER_WIDTH       (C_M_AXI_AWUSER_WIDTH       ),
        .C_M_AXI_ARUSER_WIDTH       (C_M_AXI_ARUSER_WIDTH       ),
        .C_M_AXI_WUSER_WIDTH        (C_M_AXI_WUSER_WIDTH        ),
        .C_M_AXI_RUSER_WIDTH        (C_M_AXI_RUSER_WIDTH        ),
        .C_M_AXI_BUSER_WIDTH        (C_M_AXI_BUSER_WIDTH        )
    )
    u_cache_top(
    	.clk            (clk            ),
        .rst            (rst            ),

        // from npc
        .icache_valid   (icache_valid   ),
        .icache_uncache (icache_uncache ),
        .icache_op      (icache_op      ),
        .icache_index   (icache_index   ),
        .icache_tag     (icache_tag     ),
        .icache_offset  (icache_offset  ),
        .icache_addr_ok (icache_addr_ok ),
        .icache_data_ok (icache_data_ok ),
        .icache_rdata   (icache_rdata   ),
        .dcache_valid   (dcache_valid   ),
        .dcache_uncache (dcache_uncache ),
        .dcache_op      (dcache_op      ),
        .dcache_index   (dcache_index   ),
        .dcache_tag     (dcache_tag     ),
        .dcache_offset  (dcache_offset  ),
        .dcache_wstrb   (dcache_wstrb   ),
        .dcache_wdata   (dcache_wdata   ),
        .dcache_addr_ok (dcache_addr_ok ),
        .dcache_data_ok (dcache_data_ok ),
        .dcache_rdata   (dcache_rdata   ),
        .es_fence           (es_fence       ),
        .es_fence_complete  (es_fence_complete),

        // AXI4
        .M_AXI_AWID     (mem_axi_awid     ),
        .M_AXI_AWADDR   (mem_axi_awaddr   ),
        .M_AXI_AWLEN    (mem_axi_awlen    ),
        .M_AXI_AWSIZE   (mem_axi_awsize   ),
        .M_AXI_AWBURST  (mem_axi_awburst  ),
        .M_AXI_AWLOCK   (mem_axi_awlock   ),
        .M_AXI_AWCACHE  (mem_axi_awcache  ),
        .M_AXI_AWPROT   (mem_axi_awprot   ),
        .M_AXI_AWQOS    (mem_axi_awqos    ),
        .M_AXI_AWREGION (mem_axi_awregion ),
        .M_AXI_AWUSER   (mem_axi_awuser   ),
        .M_AXI_AWVALID  (mem_axi_awvalid  ),
        .M_AXI_AWREADY  (mem_axi_awready  ),
        .M_AXI_WDATA    (mem_axi_wdata    ),
        .M_AXI_WSTRB    (mem_axi_wstrb    ),
        .M_AXI_WLAST    (mem_axi_wlast    ),
        .M_AXI_WUSER    (mem_axi_wuser    ),
        .M_AXI_WVALID   (mem_axi_wvalid   ),
        .M_AXI_WREADY   (mem_axi_wready   ),
        .M_AXI_BID      (mem_axi_bid      ),
        .M_AXI_BRESP    (mem_axi_bresp    ),
        .M_AXI_BUSER    (mem_axi_buser    ),
        .M_AXI_BVALID   (mem_axi_bvalid   ),
        .M_AXI_BREADY   (mem_axi_bready   ),
        .M_AXI_ARID     (mem_axi_arid     ),
        .M_AXI_ARADDR   (mem_axi_araddr   ),
        .M_AXI_ARLEN    (mem_axi_arlen    ),
        .M_AXI_ARSIZE   (mem_axi_arsize   ),
        .M_AXI_ARBURST  (mem_axi_arburst  ),
        .M_AXI_ARLOCK   (mem_axi_arlock   ),
        .M_AXI_ARCACHE  (mem_axi_arcache  ),
        .M_AXI_ARPROT   (mem_axi_arprot   ),
        .M_AXI_ARQOS    (mem_axi_arqos    ),
        .M_AXI_ARREGION (mem_axi_arregion ),
        .M_AXI_ARUSER   (mem_axi_aruser   ),
        .M_AXI_ARVALID  (mem_axi_arvalid  ),
        .M_AXI_ARREADY  (mem_axi_arready  ),
        .M_AXI_RID      (mem_axi_rid      ),
        .M_AXI_RDATA    (mem_axi_rdata    ),
        .M_AXI_RRESP    (mem_axi_rresp    ),
        .M_AXI_RLAST    (mem_axi_rlast    ),
        .M_AXI_RUSER    (mem_axi_ruser    ),
        .M_AXI_RVALID   (mem_axi_rvalid   ),
        .M_AXI_RREADY   (mem_axi_rready   ),

        .icache_ram0_bank0_Q       (icache_ram0_bank0_Q    ),
        .icache_ram0_bank0_CEN     (icache_ram0_bank0_CEN  ),
        .icache_ram0_bank0_WEN     (icache_ram0_bank0_WEN  ),
        .icache_ram0_bank0_BWEN    (icache_ram0_bank0_BWEN ),
        .icache_ram0_bank0_A       (icache_ram0_bank0_A    ),
        .icache_ram0_bank0_D       (icache_ram0_bank0_D    ),
        .icache_ram0_bank1_Q       (icache_ram0_bank1_Q    ),
        .icache_ram0_bank1_CEN     (icache_ram0_bank1_CEN  ),
        .icache_ram0_bank1_WEN     (icache_ram0_bank1_WEN  ),
        .icache_ram0_bank1_BWEN    (icache_ram0_bank1_BWEN ),
        .icache_ram0_bank1_A       (icache_ram0_bank1_A    ),
        .icache_ram0_bank1_D       (icache_ram0_bank1_D    ),
        .icache_ram1_bank0_Q       (icache_ram1_bank0_Q    ),
        .icache_ram1_bank0_CEN     (icache_ram1_bank0_CEN  ),
        .icache_ram1_bank0_WEN     (icache_ram1_bank0_WEN  ),
        .icache_ram1_bank0_BWEN    (icache_ram1_bank0_BWEN ),
        .icache_ram1_bank0_A       (icache_ram1_bank0_A    ),
        .icache_ram1_bank0_D       (icache_ram1_bank0_D    ),
        .icache_ram1_bank1_Q       (icache_ram1_bank1_Q    ),
        .icache_ram1_bank1_CEN     (icache_ram1_bank1_CEN  ),
        .icache_ram1_bank1_WEN     (icache_ram1_bank1_WEN  ),
        .icache_ram1_bank1_BWEN    (icache_ram1_bank1_BWEN ),
        .icache_ram1_bank1_A       (icache_ram1_bank1_A    ),
        .icache_ram1_bank1_D       (icache_ram1_bank1_D    ),

        .dcache_ram0_bank0_Q       (dcache_ram0_bank0_Q    ),
        .dcache_ram0_bank0_CEN     (dcache_ram0_bank0_CEN  ),
        .dcache_ram0_bank0_WEN     (dcache_ram0_bank0_WEN  ),
        .dcache_ram0_bank0_BWEN    (dcache_ram0_bank0_BWEN ),
        .dcache_ram0_bank0_A       (dcache_ram0_bank0_A    ),
        .dcache_ram0_bank0_D       (dcache_ram0_bank0_D    ),
        .dcache_ram0_bank1_Q       (dcache_ram0_bank1_Q    ),
        .dcache_ram0_bank1_CEN     (dcache_ram0_bank1_CEN  ),
        .dcache_ram0_bank1_WEN     (dcache_ram0_bank1_WEN  ),
        .dcache_ram0_bank1_BWEN    (dcache_ram0_bank1_BWEN ),
        .dcache_ram0_bank1_A       (dcache_ram0_bank1_A    ),
        .dcache_ram0_bank1_D       (dcache_ram0_bank1_D    ),
        .dcache_ram1_bank0_Q       (dcache_ram1_bank0_Q    ),
        .dcache_ram1_bank0_CEN     (dcache_ram1_bank0_CEN  ),
        .dcache_ram1_bank0_WEN     (dcache_ram1_bank0_WEN  ),
        .dcache_ram1_bank0_BWEN    (dcache_ram1_bank0_BWEN ),
        .dcache_ram1_bank0_A       (dcache_ram1_bank0_A    ),
        .dcache_ram1_bank0_D       (dcache_ram1_bank0_D    ),
        .dcache_ram1_bank1_Q       (dcache_ram1_bank1_Q    ),
        .dcache_ram1_bank1_CEN     (dcache_ram1_bank1_CEN  ),
        .dcache_ram1_bank1_WEN     (dcache_ram1_bank1_WEN  ),
        .dcache_ram1_bank1_BWEN    (dcache_ram1_bank1_BWEN ),
        .dcache_ram1_bank1_A       (dcache_ram1_bank1_A    ),
        .dcache_ram1_bank1_D       (dcache_ram1_bank1_D    )
    );
    

    
    assign io_slave_awready = 'b0;
   
    assign io_slave_wready = 'b0;
    assign io_slave_bid = 'b0;
    assign io_slave_bresp = 'b0;
    assign io_slave_buser = 'b0;
    assign io_slave_bvalid = 'b0;
   
    assign io_slave_arready = 'b0;
    assign io_slave_rid = 'b0;
    assign io_slave_rdata = 'b0;
    assign io_slave_rresp = 'b0;
    assign io_slave_rlast = 'b0;
    assign io_slave_ruser = 'b0;
    assign io_slave_rvalid = 'b0;

endmodule


module ysyx_040991_RegisterFile # (
    parameter ysyx_040991_DATA_WIDTH = 64
)(
    input clk,
    // READ PORT 1
    input [4:0] raddr1,
    output[ysyx_040991_DATA_WIDTH-1:0] rdata1,
    // READ PORT 2
    input [4:0] raddr2,
    output[ysyx_040991_DATA_WIDTH-1:0] rdata2,
    // WRITE PORT1
    input [ysyx_040991_DATA_WIDTH-1:0] wb_data,
    input [4:0] wb_addr,
    input wen
);

    reg [ysyx_040991_DATA_WIDTH-1:0] rf [0:31];
    integer i;
    initial begin
        for(i=0; i<31; i=i+1) begin
            rf[i] = 64'b0;
        end
    end
    always @ (posedge clk) begin
        if (wen) begin
            rf[wb_addr] <= (wb_addr == 5'b0) ? 64'b0 : wb_data;
        end
    end

    assign rdata1 = (raddr1 == 5'b0) ? 64'b0 : rf[raddr1];
    assign rdata2 = (raddr2 == 5'b0) ? 64'b0 : rf[raddr2];

    
endmodule

module ysyx_040991_tag_v_ram (
    input                           clk ,
    input                           rst ,
    input                           cen ,
    input [`ysyx_040991_CACHE_INDEX_WD  -1:0]   addr,
    input [`ysyx_040991_CACHE_TAG_WD      :0]   din ,
    input                           wen ,
    
    output reg [`ysyx_040991_CACHE_TAG_WD :0]   dout
);

    integer i;
    reg [`ysyx_040991_CACHE_TAG_WD:0] ram [0:2**`ysyx_040991_CACHE_INDEX_WD-1];
    always @(posedge clk) begin
        if (rst) begin
            for (i = 0; i < 2**`ysyx_040991_CACHE_INDEX_WD; i=i+1) begin
                ram[i] <= 'b0;
            end
        end
        else if (wen && cen) begin
            ram[addr] <= din;
        end
        dout <= cen ? ram[addr] : 'hffff;
    end
        
endmodule

module ysyx_040991_wallace_tree (
    input [16:0] din,
    input [13:0] cin,
    output [13:0] cout,
    output c_final, 
    output s_final
);
    wire [4:0]  s0;
    wire [3:0]  s1;
    wire [1:0]  s2;
    wire [1:0]  s3;
    wire        s4;
    
    ysyx_040991_adder3 u_adder00 (.a(din[4]),   .b(din[3]),     .cin(din[2]),   .cout(cout[0]),     .s(s0[0]));
    ysyx_040991_adder3 u_adder01 (.a(din[7]),   .b(din[6]),     .cin(din[5]),   .cout(cout[1]),     .s(s0[1]));
    ysyx_040991_adder3 u_adder02 (.a(din[10]),  .b(din[9]),     .cin(din[8]),   .cout(cout[2]),     .s(s0[2]));
    ysyx_040991_adder3 u_adder03 (.a(din[13]),  .b(din[12]),    .cin(din[11]),  .cout(cout[3]),     .s(s0[3]));
    ysyx_040991_adder3 u_adder04 (.a(din[16]),  .b(din[15]),    .cin(din[14]),  .cout(cout[4]),     .s(s0[4]));

    ysyx_040991_adder3 u_adder10 (.a(cin[2]),   .b(cin[1]),     .cin(cin[0]),   .cout(cout[5]),     .s(s1[0]));
    ysyx_040991_adder3 u_adder11 (.a(din[0]),   .b(cin[4]),     .cin(cin[3]),   .cout(cout[6]),     .s(s1[1]));
    ysyx_040991_adder3 u_adder12 (.a(s0[1]),    .b(s0[0]),      .cin(din[1]),   .cout(cout[7]),     .s(s1[2]));
    ysyx_040991_adder3 u_adder13 (.a(s0[4]),    .b(s0[3]),      .cin(s0[2]),    .cout(cout[8]),     .s(s1[3]));

    ysyx_040991_adder3 u_adder20 (.a(s1[0]),    .b(cin[6]),     .cin(cin[5]),   .cout(cout[9]),     .s(s2[0]));
    ysyx_040991_adder3 u_adder21 (.a(s1[3]),    .b(s1[2]),      .cin(s1[1]),    .cout(cout[10]),    .s(s2[1]));

    ysyx_040991_adder3 u_adder30 (.a(cin[9]),   .b(cin[8]),     .cin(cin[7]),   .cout(cout[11]),    .s(s3[0]));
    ysyx_040991_adder3 u_adder31 (.a(s2[1]),    .b(s2[0]),      .cin(cin[10]),  .cout(cout[12]),    .s(s3[1]));

    ysyx_040991_adder3 u_adder40 (.a(s3[1]),    .b(s3[0]),      .cin(cin[11]),  .cout(cout[13]),    .s(s4));

    ysyx_040991_adder3 u_adder50 (.a(s4),       .b(cin[13]),    .cin(cin[12]),  .cout(c_final),     .s(s_final));
    
endmodule

module ysyx_040991_wb_stage(
    input                           clk           ,
    input                           rst         ,
    //allowin
    output                          ws_allowin    ,
    //from ms
    input                           ms_to_ws_valid,
    input  [`ysyx_040991_MS_TO_WS_BUS_WD -1:0]  ms_to_ws_bus  ,
    input  [`ysyx_040991_EXCEPTION_WD      -1:0]  ms_to_ws_exception,
    //to rf: for write back
    output [`ysyx_040991_WS_TO_RF_BUS_WD -1:0]  ws_to_rf_bus  ,
    // forwarding bus
    output  [`ysyx_040991_WS_FORWARDING_BUS_WD -1:0] ws_forwarding_bus,
    // to es csr forwarding bus
    output [`ysyx_040991_CSR_FORWARDING_BUS_WD -1:0] ws_csr_forwarding_bus,
    // to csr
    output csr_wen,
    output [11:0] csr_waddr,
    output [63:0] csr_wdata,
    output [5:0] ws_exception,

    output ws_is_exception,
    output ws_is_eret,
    output ws_exception_valid,
    output [63:0]  ws_exception_pc
);

    reg         ws_valid;
    wire        ws_ready_go;

    reg [`ysyx_040991_MS_TO_WS_BUS_WD -1:0] ms_to_ws_bus_r;
    reg [`ysyx_040991_EXCEPTION_WD   -1:0] ms_to_ws_exception_r;
    wire ws_rd_wen       ;
    wire [4:0] ws_rd_addr      ;
    wire [`ysyx_040991_DATA_WIDTH-1:0] ws_final_result ;
    wire [63:0] ws_pc           ;
    wire [31:0] ws_inst;
    wire [`ysyx_040991_DATA_WIDTH-1:0] ws_alu_result;
    wire ws_csr_wen;
    // wire ws_dcache_uncache;
    assign {
        // ws_dcache_uncache   ,
        ws_rd_wen           ,      
        ws_rd_addr          ,     
        ws_final_result     ,
        ws_alu_result       ,
        ws_csr_wen          ,
        ws_pc               ,
        ws_inst        
    } = ms_to_ws_bus_r;

    wire        rf_wen;
    wire [4 :0] rf_waddr;
    wire [`ysyx_040991_DATA_WIDTH-1:0] rf_wdata;
    assign ws_to_rf_bus = {
        rf_wen      ,   
        rf_waddr    ,   
        rf_wdata        
    };

    assign ws_is_exception =  ws_valid && ((ws_exception[5:4] == `ysyx_040991_EXCEPTION_EXCEPTION) || (ws_exception[5:4] == `ysyx_040991_EXCEPTION_INTERRUPT));
    assign ws_is_eret =  ws_valid && (ws_exception[5:4] == `ysyx_040991_EXCEPTION_ERET);
    assign ws_exception_valid = ws_valid;
    wire ws_flush;
    assign ws_flush = ws_is_eret || ws_is_exception;
    reg ws_flush_r;
    always @(posedge clk) begin
        if (rst) begin
            ws_flush_r <= 1'b0;
        end else if (ws_is_eret || ws_is_exception) begin
            ws_flush_r <= 1'b1;
        end else if (ms_to_ws_valid && ws_allowin) begin
            ws_flush_r <= 1'b0;
        end 
    end
    assign ws_ready_go = 1'b1;
    assign ws_allowin  = !ws_valid || ws_ready_go;
    always @(posedge clk) begin
        if (rst) begin
            ws_valid <= 1'b0;
        end
        else if (ws_allowin) begin
            ws_valid <= ms_to_ws_valid;
        end

        if (ms_to_ws_valid && ws_allowin) begin
            ms_to_ws_bus_r <= ms_to_ws_bus;
            ms_to_ws_exception_r <= ms_to_ws_exception;
        end
    end

    assign rf_wen   = ws_rd_wen && ws_valid && !(ws_flush || ws_flush_r);
    assign rf_waddr = ws_rd_addr;
    assign rf_wdata = ws_final_result;

    assign csr_wen = ws_csr_wen && ws_valid;
    assign csr_waddr = ws_inst[31:20];
    assign csr_wdata = ws_alu_result;

    assign ws_forwarding_bus = {
        rf_wen,
        rf_waddr,
        rf_wdata
    };

    assign ws_csr_forwarding_bus = {
        csr_wen,
        csr_waddr,
        csr_wdata
    };

    assign ws_exception = ms_to_ws_exception_r;
    assign ws_exception_pc = ws_pc;
    
endmodule

module ysyx_040991 (
    input clock,
    input reset,
    input io_interrupt,
    input	io_master_awready,
    output	io_master_awvalid,
    output [3:0]	io_master_awid,
    output [31:0]	io_master_awaddr,
    output [7:0]	io_master_awlen,
    output [2:0]	io_master_awsize,
    output [1:0]	io_master_awburst,
    input	io_master_wready,
    output	io_master_wvalid,
    output [63:0]	io_master_wdata,
    output [7:0]	io_master_wstrb,
    output	io_master_wlast,
    output	io_master_bready,
    input	io_master_bvalid,
    input [3:0]	io_master_bid,
    input [1:0]	io_master_bresp,
    input	io_master_arready,
    output	io_master_arvalid,
    output [3:0]	io_master_arid,
    output [31:0]	io_master_araddr,
    output [7:0]	io_master_arlen,
    output [2:0]	io_master_arsize,
    output [1:0]	io_master_arburst,
    output	io_master_rready,
    input	io_master_rvalid,
    input [3:0]	io_master_rid,
    input [1:0]	io_master_rresp,
    input [63:0]	io_master_rdata,
    input	io_master_rlast,

    output	io_slave_awready,
    input	io_slave_awvalid,
    input [3:0]	io_slave_awid,
    input [31:0]	io_slave_awaddr,
    input [7:0]	io_slave_awlen,
    input [2:0]	io_slave_awsize,
    input [1:0]	io_slave_awburst,
    output	io_slave_wready,
    input	io_slave_wvalid,
    input [63:0]	io_slave_wdata,
    input [7:0]	io_slave_wstrb,
    input	io_slave_wlast,
    input	io_slave_bready,
    output	io_slave_bvalid,
    output [3:0]	io_slave_bid,
    output [1:0]	io_slave_bresp,
    output	io_slave_arready,
    input	io_slave_arvalid,
    input [3:0]	io_slave_arid,
    input [31:0]	io_slave_araddr,
    input [7:0]	io_slave_arlen,
    input [2:0]	io_slave_arsize,
    input [1:0]	io_slave_arburst,
    input	io_slave_rready,
    output	io_slave_rvalid,
    output [3:0]	io_slave_rid,
    output [1:0]	io_slave_rresp,
    output [63:0]	io_slave_rdata,
    output	io_slave_rlast,

    output [5:0]	io_sram0_addr,
    output	        io_sram0_cen,
    output	        io_sram0_wen,
    output [127:0]	io_sram0_wmask,
    output [127:0]	io_sram0_wdata,
    input [127:0]	io_sram0_rdata,	

    output [5:0]	io_sram1_addr,
    output	        io_sram1_cen,
    output	        io_sram1_wen,
    output [127:0]	io_sram1_wmask,
    output [127:0]	io_sram1_wdata,
    input [127:0]	io_sram1_rdata,	

    output [5:0]	io_sram2_addr,
    output	        io_sram2_cen,
    output	        io_sram2_wen,
    output [127:0]	io_sram2_wmask,
    output [127:0]	io_sram2_wdata,
    input [127:0]	io_sram2_rdata,	

    output [5:0]	io_sram3_addr,
    output	        io_sram3_cen,
    output	        io_sram3_wen,
    output [127:0]	io_sram3_wmask,
    output [127:0]	io_sram3_wdata,
    input [127:0]	io_sram3_rdata,	

    output [5:0]	io_sram4_addr,
    output	        io_sram4_cen,
    output	        io_sram4_wen,
    output [127:0]	io_sram4_wmask,
    output [127:0]	io_sram4_wdata,
    input [127:0]	io_sram4_rdata,	

    output [5:0]	io_sram5_addr,
    output	        io_sram5_cen,
    output	        io_sram5_wen,
    output [127:0]	io_sram5_wmask,
    output [127:0]	io_sram5_wdata,
    input [127:0]	io_sram5_rdata,	

    output [5:0]	io_sram6_addr,
    output	        io_sram6_cen,
    output	        io_sram6_wen,
    output [127:0]	io_sram6_wmask,
    output [127:0]	io_sram6_wdata,
    input [127:0]	io_sram6_rdata,	

    output [5:0]	io_sram7_addr,
    output	        io_sram7_cen,
    output	        io_sram7_wen,
    output [127:0]	io_sram7_wmask,
    output [127:0]	io_sram7_wdata,
    input [127:0]	io_sram7_rdata

    );
    
    parameter C_M_AXI_ID_WIDTH	    = 4;
    parameter C_M_AXI_ADDR_WIDTH	= 32;
    parameter C_M_AXI_ysyx_040991_DATA_WIDTH	= 64;
    parameter C_M_AXI_AWUSER_WIDTH	= 1;
    parameter C_M_AXI_ARUSER_WIDTH	= 1;
    parameter C_M_AXI_WUSER_WIDTH	= 1;
    parameter C_M_AXI_RUSER_WIDTH	= 1;
    parameter C_M_AXI_BUSER_WIDTH	= 1;
    wire [C_M_AXI_ID_WIDTH-1 : 0]        mem_axi_awid;
    wire [C_M_AXI_ADDR_WIDTH-1 : 0]      mem_axi_awaddr;
    wire [7 : 0]                         mem_axi_awlen;
    wire [2 : 0]                         mem_axi_awsize;
    wire [1 : 0]                         mem_axi_awburst;
    wire                                 mem_axi_awlock;
    wire [3 : 0]                         mem_axi_awcache;
    wire [2 : 0]                         mem_axi_awprot;
    wire [3 : 0]                         mem_axi_awqos;
    wire [3 : 0]                         mem_axi_awregion;
    wire [C_M_AXI_AWUSER_WIDTH-1 : 0]    mem_axi_awuser;
    wire                                 mem_axi_awvalid;
    wire                                 mem_axi_awready;
    wire [C_M_AXI_ysyx_040991_DATA_WIDTH-1 : 0]      mem_axi_wdata;
    wire [C_M_AXI_ysyx_040991_DATA_WIDTH/8-1 : 0]    mem_axi_wstrb;
    wire                                 mem_axi_wlast;
    wire [C_M_AXI_WUSER_WIDTH-1 : 0]     mem_axi_wuser;
    wire                                 mem_axi_wvalid;
    wire                                 mem_axi_wready;
    wire [C_M_AXI_ID_WIDTH-1 : 0]        mem_axi_bid;
    wire [1 : 0]                         mem_axi_bresp;
    wire [C_M_AXI_BUSER_WIDTH-1 : 0]     mem_axi_buser;
    wire                                 mem_axi_bvalid;
    wire                                 mem_axi_bready;
    wire [C_M_AXI_ID_WIDTH-1 : 0]        mem_axi_arid;
    wire [C_M_AXI_ADDR_WIDTH-1 : 0]      mem_axi_araddr;
    wire [7 : 0]                         mem_axi_arlen;
    wire [2 : 0]                         mem_axi_arsize;
    wire [1 : 0]                         mem_axi_arburst;
    wire                                 mem_axi_arlock;
    wire [3 : 0]                         mem_axi_arcache;
    wire [2 : 0]                         mem_axi_arprot;
    wire [3 : 0]                         mem_axi_arqos;
    wire [3 : 0]                         mem_axi_arregion;
    wire [C_M_AXI_ARUSER_WIDTH-1 : 0]    mem_axi_aruser;
    wire                                 mem_axi_arvalid;
    wire                                 mem_axi_arready;
    wire [C_M_AXI_ID_WIDTH-1 : 0]        mem_axi_rid;
    wire [C_M_AXI_ysyx_040991_DATA_WIDTH-1 : 0]      mem_axi_rdata;
    wire [1 : 0]                         mem_axi_rresp;
    wire                                 mem_axi_rlast;
    wire [C_M_AXI_RUSER_WIDTH-1 : 0]     mem_axi_ruser;
    wire                                 mem_axi_rvalid;
    wire                                 mem_axi_rready;

    wire [C_M_AXI_ID_WIDTH-1 : 0]        clint_axi_awid;
    wire [C_M_AXI_ADDR_WIDTH-1 : 0]      clint_axi_awaddr;
    wire [7 : 0]                         clint_axi_awlen;
    wire [2 : 0]                         clint_axi_awsize;
    wire [1 : 0]                         clint_axi_awburst;
    wire                                 clint_axi_awlock;
    wire [3 : 0]                         clint_axi_awcache;
    wire [2 : 0]                         clint_axi_awprot;
    wire [3 : 0]                         clint_axi_awqos;
    wire [3 : 0]                         clint_axi_awregion;
    wire [C_M_AXI_AWUSER_WIDTH-1 : 0]    clint_axi_awuser;
    wire                                 clint_axi_awvalid;
    wire                                 clint_axi_awready;
    wire [C_M_AXI_ysyx_040991_DATA_WIDTH-1 : 0]      clint_axi_wdata;
    wire [C_M_AXI_ysyx_040991_DATA_WIDTH/8-1 : 0]    clint_axi_wstrb;
    wire                                 clint_axi_wlast;
    wire [C_M_AXI_WUSER_WIDTH-1 : 0]     clint_axi_wuser;
    wire                                 clint_axi_wvalid;
    wire                                 clint_axi_wready;
    wire [C_M_AXI_ID_WIDTH-1 : 0]        clint_axi_bid;
    wire [1 : 0]                         clint_axi_bresp;
    wire [C_M_AXI_BUSER_WIDTH-1 : 0]     clint_axi_buser;
    wire                                 clint_axi_bvalid;
    wire                                 clint_axi_bready;
    wire [C_M_AXI_ID_WIDTH-1 : 0]        clint_axi_arid;
    wire [C_M_AXI_ADDR_WIDTH-1 : 0]      clint_axi_araddr;
    wire [7 : 0]                         clint_axi_arlen;
    wire [2 : 0]                         clint_axi_arsize;
    wire [1 : 0]                         clint_axi_arburst;
    wire                                 clint_axi_arlock;
    wire [3 : 0]                         clint_axi_arcache;
    wire [2 : 0]                         clint_axi_arprot;
    wire [3 : 0]                         clint_axi_arqos;
    wire [3 : 0]                         clint_axi_arregion;
    wire [C_M_AXI_ARUSER_WIDTH-1 : 0]    clint_axi_aruser;
    wire                                 clint_axi_arvalid;
    wire                                 clint_axi_arready;
    wire [C_M_AXI_ID_WIDTH-1 : 0]        clint_axi_rid;
    wire [C_M_AXI_ysyx_040991_DATA_WIDTH-1 : 0]      clint_axi_rdata;
    wire [1 : 0]                         clint_axi_rresp;
    wire                                 clint_axi_rlast;
    wire [C_M_AXI_RUSER_WIDTH-1 : 0]     clint_axi_ruser;
    wire                                 clint_axi_rvalid;
    wire                                 clint_axi_rready;

    wire timer_interrupt;
    wire io_slave_awlock   ;
    wire [3:0] io_slave_awcache  ;
    wire [2:0] io_slave_awprot   ;
    wire [3:0] io_slave_awqos    ;
    wire [3:0] io_slave_awregion ;
    wire io_slave_awuser   ;
    wire io_slave_arlock   ;
    wire [3:0] io_slave_arcache  ;
    wire [2:0] io_slave_arprot   ;
    wire [3:0] io_slave_arqos    ;
    wire [3:0] io_slave_arregion ;
    wire io_slave_awuser   ;
    wire io_slave_wuser;
    wire io_slave_buser;
    wire io_slave_aruser;
    wire io_slave_ruser;
    assign io_slave_wuser = 'b0;
    assign io_slave_aruser = 'b0;
    assign io_slave_awlock = 'b0;
    assign io_slave_awcache = 'b0;
    assign io_slave_awprot = 'b0;
    assign io_slave_awqos = 'b0;
    assign io_slave_awregion = 'b0;
    assign io_slave_awuser = 'b0;
    assign io_slave_arlock = 'b0;
    assign io_slave_arcache = 'b0;
    assign io_slave_arprot = 'b0;
    assign io_slave_arqos = 'b0;
    assign io_slave_arregion = 'b0;
    ysyx_040991_npc_top  u_npc_top(
    	.clk               (clock               ),
        .rst               (reset               ),
        .interrupt_taken   (timer_interrupt   ),
        .mem_axi_awid      (mem_axi_awid    ),
        .mem_axi_awaddr    (mem_axi_awaddr  ),
        .mem_axi_awlen     (mem_axi_awlen   ),
        .mem_axi_awsize    (mem_axi_awsize  ),
        .mem_axi_awburst   (mem_axi_awburst ),
        .mem_axi_awlock    (mem_axi_awlock  ),
        .mem_axi_awcache   (mem_axi_awcache ),
        .mem_axi_awprot    (mem_axi_awprot  ),
        .mem_axi_awqos     (mem_axi_awqos   ),
        .mem_axi_awregion  (mem_axi_awregion),
        .mem_axi_awuser    (mem_axi_awuser  ),
        .mem_axi_awvalid   (mem_axi_awvalid ),
        .mem_axi_awready   (mem_axi_awready ),
        .mem_axi_wdata     (mem_axi_wdata   ),
        .mem_axi_wstrb     (mem_axi_wstrb   ),
        .mem_axi_wlast     (mem_axi_wlast   ),
        .mem_axi_wuser     (mem_axi_wuser   ),
        .mem_axi_wvalid    (mem_axi_wvalid  ),
        .mem_axi_wready    (mem_axi_wready  ),
        .mem_axi_bid       (mem_axi_bid     ),
        .mem_axi_bresp     (mem_axi_bresp   ),
        .mem_axi_buser     (mem_axi_buser   ),
        .mem_axi_bvalid    (mem_axi_bvalid  ),
        .mem_axi_bready    (mem_axi_bready  ),
        .mem_axi_arid      (mem_axi_arid    ),
        .mem_axi_araddr    (mem_axi_araddr  ),
        .mem_axi_arlen     (mem_axi_arlen   ),
        .mem_axi_arsize    (mem_axi_arsize  ),
        .mem_axi_arburst   (mem_axi_arburst ),
        .mem_axi_arlock    (mem_axi_arlock  ),
        .mem_axi_arcache   (mem_axi_arcache ),
        .mem_axi_arprot    (mem_axi_arprot  ),
        .mem_axi_arqos     (mem_axi_arqos   ),
        .mem_axi_arregion  (mem_axi_arregion),
        .mem_axi_aruser    (mem_axi_aruser  ),
        .mem_axi_arvalid   (mem_axi_arvalid ),
        .mem_axi_arready   (mem_axi_arready ),
        .mem_axi_rid       (mem_axi_rid     ),
        .mem_axi_rdata     (mem_axi_rdata   ),
        .mem_axi_rresp     (mem_axi_rresp   ),
        .mem_axi_rlast     (mem_axi_rlast   ),
        .mem_axi_ruser     (mem_axi_ruser   ),
        .mem_axi_rvalid    (mem_axi_rvalid  ),
        .mem_axi_rready    (mem_axi_rready  ),
        .io_slave_awid     (io_slave_awid           ),
        .io_slave_awaddr   (io_slave_awaddr         ),
        .io_slave_awlen    (io_slave_awlen          ),
        .io_slave_awsize   (io_slave_awsize         ),
        .io_slave_awburst  (io_slave_awburst        ),
        .io_slave_awlock   (io_slave_awlock         ),
        .io_slave_awcache  (io_slave_awcache        ),
        .io_slave_awprot   (io_slave_awprot         ),
        .io_slave_awqos    (io_slave_awqos          ),
        .io_slave_awregion (io_slave_awregion       ),
        .io_slave_awuser   (io_slave_awuser         ),
        .io_slave_awvalid  (io_slave_awvalid        ),
        .io_slave_awready  (io_slave_awready        ),
        .io_slave_wdata    (io_slave_wdata          ),
        .io_slave_wstrb    (io_slave_wstrb          ),
        .io_slave_wlast    (io_slave_wlast          ),
        .io_slave_wuser    (io_slave_wuser          ),
        .io_slave_wvalid   (io_slave_wvalid         ),
        .io_slave_wready   (io_slave_wready         ),
        .io_slave_bid      (io_slave_bid            ),
        .io_slave_bresp    (io_slave_bresp          ),
        .io_slave_buser    (io_slave_buser          ),
        .io_slave_bvalid   (io_slave_bvalid         ),
        .io_slave_bready   (io_slave_bready         ),
        .io_slave_arid     (io_slave_arid           ),
        .io_slave_araddr   (io_slave_araddr         ),
        .io_slave_arlen    (io_slave_arlen          ),
        .io_slave_arsize   (io_slave_arsize         ),
        .io_slave_arburst  (io_slave_arburst        ),
        .io_slave_arlock   (io_slave_arlock         ),
        .io_slave_arcache  (io_slave_arcache        ),
        .io_slave_arprot   (io_slave_arprot         ),
        .io_slave_arqos    (io_slave_arqos          ),
        .io_slave_arregion (io_slave_arregion       ),
        .io_slave_aruser   (io_slave_aruser         ),
        .io_slave_arvalid  (io_slave_arvalid        ),
        .io_slave_arready  (io_slave_arready        ),
        .io_slave_rid      (io_slave_rid            ),
        .io_slave_rdata    (io_slave_rdata          ),
        .io_slave_rresp    (io_slave_rresp          ),
        .io_slave_rlast    (io_slave_rlast          ),
        .io_slave_ruser    (io_slave_ruser          ),
        .io_slave_rvalid   (io_slave_rvalid         ),
        .io_slave_rready   (io_slave_rready         ),

        .icache_ram0_bank0_Q       (io_sram0_rdata  ),
        .icache_ram0_bank0_CEN     (io_sram0_cen    ),
        .icache_ram0_bank0_WEN     (io_sram0_wen    ),
        .icache_ram0_bank0_BWEN    (io_sram0_wmask  ),
        .icache_ram0_bank0_A       (io_sram0_addr   ),
        .icache_ram0_bank0_D       (io_sram0_wdata  ),

        .icache_ram0_bank1_Q       (io_sram1_rdata  ),
        .icache_ram0_bank1_CEN     (io_sram1_cen    ),
        .icache_ram0_bank1_WEN     (io_sram1_wen    ),
        .icache_ram0_bank1_BWEN    (io_sram1_wmask  ),
        .icache_ram0_bank1_A       (io_sram1_addr   ),
        .icache_ram0_bank1_D       (io_sram1_wdata  ),

        .icache_ram1_bank0_Q       (io_sram2_rdata  ),
        .icache_ram1_bank0_CEN     (io_sram2_cen    ),
        .icache_ram1_bank0_WEN     (io_sram2_wen    ),
        .icache_ram1_bank0_BWEN    (io_sram2_wmask  ),
        .icache_ram1_bank0_A       (io_sram2_addr   ),
        .icache_ram1_bank0_D       (io_sram2_wdata  ),

        .icache_ram1_bank1_Q       (io_sram3_rdata  ),
        .icache_ram1_bank1_CEN     (io_sram3_cen    ),
        .icache_ram1_bank1_WEN     (io_sram3_wen    ),
        .icache_ram1_bank1_BWEN    (io_sram3_wmask  ),
        .icache_ram1_bank1_A       (io_sram3_addr   ),
        .icache_ram1_bank1_D       (io_sram3_wdata  ),

        .dcache_ram0_bank0_Q       (io_sram4_rdata  ),
        .dcache_ram0_bank0_CEN     (io_sram4_cen    ),
        .dcache_ram0_bank0_WEN     (io_sram4_wen    ),
        .dcache_ram0_bank0_BWEN    (io_sram4_wmask  ),
        .dcache_ram0_bank0_A       (io_sram4_addr   ),
        .dcache_ram0_bank0_D       (io_sram4_wdata  ),

        .dcache_ram0_bank1_Q       (io_sram5_rdata  ),
        .dcache_ram0_bank1_CEN     (io_sram5_cen    ),
        .dcache_ram0_bank1_WEN     (io_sram5_wen    ),
        .dcache_ram0_bank1_BWEN    (io_sram5_wmask  ),
        .dcache_ram0_bank1_A       (io_sram5_addr   ),
        .dcache_ram0_bank1_D       (io_sram5_wdata  ),

        .dcache_ram1_bank0_Q       (io_sram6_rdata  ),
        .dcache_ram1_bank0_CEN     (io_sram6_cen    ),
        .dcache_ram1_bank0_WEN     (io_sram6_wen    ),
        .dcache_ram1_bank0_BWEN    (io_sram6_wmask  ),
        .dcache_ram1_bank0_A       (io_sram6_addr   ),
        .dcache_ram1_bank0_D       (io_sram6_wdata  ),

        .dcache_ram1_bank1_Q       (io_sram7_rdata  ),
        .dcache_ram1_bank1_CEN     (io_sram7_cen    ),
        .dcache_ram1_bank1_WEN     (io_sram7_wen    ),
        .dcache_ram1_bank1_BWEN    (io_sram7_wmask  ),
        .dcache_ram1_bank1_A       (io_sram7_addr   ),
        .dcache_ram1_bank1_D       (io_sram7_wdata  )
    );

    wire io_master_awlock   ;
    wire [3:0] io_master_awcache  ;
    wire [2:0] io_master_awprot   ;
    wire [3:0] io_master_awqos    ;
    wire [3:0] io_master_awregion ;
    wire io_master_awuser   ;
    wire io_master_arlock   ;
    wire [3:0] io_master_arcache  ;
    wire [2:0] io_master_arprot   ;
    wire [3:0] io_master_arqos    ;
    wire [3:0] io_master_arregion ;
    wire io_master_awuser   ;
    wire io_master_wuser;
    wire io_master_buser;
    wire io_master_aruser;
    wire io_master_ruser;
    assign io_master_buser = 'b0;
    assign io_master_ruser = 'b0;
    ysyx_040991_axi_xbar 
    #(
        .ysyx_040991_DATA_WIDTH   (64   ),
        .ADDR_WIDTH   (32   ),
        .STRB_WIDTH   (8    ),
        .S_ID_WIDTH   (4    ),
        .M_ID_WIDTH   (4    )
    )
    u_axi_xbar(
        .clk                (clock                ),
        .rst                (reset                ),
        .io_master_awid       (io_master_awid       ),
        .io_master_awaddr     (io_master_awaddr     ),
        .io_master_awlen      (io_master_awlen      ),
        .io_master_awsize     (io_master_awsize     ),
        .io_master_awburst    (io_master_awburst    ),
        .io_master_awlock     (io_master_awlock     ),
        .io_master_awcache    (io_master_awcache    ),
        .io_master_awprot     (io_master_awprot     ),
        .io_master_awqos      (io_master_awqos      ),
        .io_master_awregion   (io_master_awregion   ),
        .io_master_awuser     (io_master_awuser     ),
        .io_master_awvalid    (io_master_awvalid    ),
        .io_master_awready    (io_master_awready    ),
        .io_master_wdata      (io_master_wdata      ),
        .io_master_wstrb      (io_master_wstrb      ),
        .io_master_wlast      (io_master_wlast      ),
        .io_master_wuser      (io_master_wuser      ),
        .io_master_wvalid     (io_master_wvalid     ),
        .io_master_wready     (io_master_wready     ),
        .io_master_bid        (io_master_bid        ),
        .io_master_bresp      (io_master_bresp      ),
        .io_master_buser      (io_master_buser      ),
        .io_master_bvalid     (io_master_bvalid     ),
        .io_master_bready     (io_master_bready     ),
        .io_master_arid       (io_master_arid       ),
        .io_master_araddr     (io_master_araddr     ),
        .io_master_arlen      (io_master_arlen      ),
        .io_master_arsize     (io_master_arsize     ),
        .io_master_arburst    (io_master_arburst    ),
        .io_master_arlock     (io_master_arlock     ),
        .io_master_arcache    (io_master_arcache    ),
        .io_master_arprot     (io_master_arprot     ),
        .io_master_arqos      (io_master_arqos      ),
        .io_master_arregion   (io_master_arregion   ),
        .io_master_aruser     (io_master_aruser     ),
        .io_master_arvalid    (io_master_arvalid    ),
        .io_master_arready    (io_master_arready    ),
        .io_master_rid        (io_master_rid        ),
        .io_master_rdata      (io_master_rdata      ),
        .io_master_rresp      (io_master_rresp      ),
        .io_master_rlast      (io_master_rlast      ),
        .io_master_ruser      (io_master_ruser      ),
        .io_master_rvalid     (io_master_rvalid     ),
        .io_master_rready     (io_master_rready     ),
        .clint_axi_awid     (clint_axi_awid     ),
        .clint_axi_awaddr   (clint_axi_awaddr   ),
        .clint_axi_awlen    (clint_axi_awlen    ),
        .clint_axi_awsize   (clint_axi_awsize   ),
        .clint_axi_awburst  (clint_axi_awburst  ),
        .clint_axi_awlock   (clint_axi_awlock   ),
        .clint_axi_awcache  (clint_axi_awcache  ),
        .clint_axi_awprot   (clint_axi_awprot   ),
        .clint_axi_awqos    (clint_axi_awqos    ),
        .clint_axi_awregion (clint_axi_awregion ),
        .clint_axi_awuser   (clint_axi_awuser   ),
        .clint_axi_awvalid  (clint_axi_awvalid  ),
        .clint_axi_awready  (clint_axi_awready  ),
        .clint_axi_wdata    (clint_axi_wdata    ),
        .clint_axi_wstrb    (clint_axi_wstrb    ),
        .clint_axi_wlast    (clint_axi_wlast    ),
        .clint_axi_wuser    (clint_axi_wuser    ),
        .clint_axi_wvalid   (clint_axi_wvalid   ),
        .clint_axi_wready   (clint_axi_wready   ),
        .clint_axi_bid      (clint_axi_bid      ),
        .clint_axi_bresp    (clint_axi_bresp    ),
        .clint_axi_buser    (clint_axi_buser    ),
        .clint_axi_bvalid   (clint_axi_bvalid   ),
        .clint_axi_bready   (clint_axi_bready   ),
        .clint_axi_arid     (clint_axi_arid     ),
        .clint_axi_araddr   (clint_axi_araddr   ),
        .clint_axi_arlen    (clint_axi_arlen    ),
        .clint_axi_arsize   (clint_axi_arsize   ),
        .clint_axi_arburst  (clint_axi_arburst  ),
        .clint_axi_arlock   (clint_axi_arlock   ),
        .clint_axi_arcache  (clint_axi_arcache  ),
        .clint_axi_arprot   (clint_axi_arprot   ),
        .clint_axi_arqos    (clint_axi_arqos    ),
        .clint_axi_arregion (clint_axi_arregion ),
        .clint_axi_aruser   (clint_axi_aruser   ),
        .clint_axi_arvalid  (clint_axi_arvalid  ),
        .clint_axi_arready  (clint_axi_arready  ),
        .clint_axi_rid      (clint_axi_rid      ),
        .clint_axi_rdata    (clint_axi_rdata    ),
        .clint_axi_rresp    (clint_axi_rresp    ),
        .clint_axi_rlast    (clint_axi_rlast    ),
        .clint_axi_ruser    (clint_axi_ruser    ),
        .clint_axi_rvalid   (clint_axi_rvalid   ),
        .clint_axi_rready   (clint_axi_rready   ),
        .m00_axi_awid       (mem_axi_awid       ),
        .m00_axi_awaddr     (mem_axi_awaddr     ),
        .m00_axi_awlen      (mem_axi_awlen      ),
        .m00_axi_awsize     (mem_axi_awsize     ),
        .m00_axi_awburst    (mem_axi_awburst    ),
        .m00_axi_awlock     (mem_axi_awlock     ),
        .m00_axi_awcache    (mem_axi_awcache    ),
        .m00_axi_awprot     (mem_axi_awprot     ),
        .m00_axi_awqos      (mem_axi_awqos      ),
        .m00_axi_awregion   (mem_axi_awregion   ),
        .m00_axi_awuser     (mem_axi_awuser     ),
        .m00_axi_awvalid    (mem_axi_awvalid    ),
        .m00_axi_awready    (mem_axi_awready    ),
        .m00_axi_wdata      (mem_axi_wdata      ),
        .m00_axi_wstrb      (mem_axi_wstrb      ),
        .m00_axi_wlast      (mem_axi_wlast      ),
        .m00_axi_wuser      (mem_axi_wuser      ),
        .m00_axi_wvalid     (mem_axi_wvalid     ),
        .m00_axi_wready     (mem_axi_wready     ),
        .m00_axi_bid        (mem_axi_bid        ),
        .m00_axi_bresp      (mem_axi_bresp      ),
        .m00_axi_buser      (mem_axi_buser      ),
        .m00_axi_bvalid     (mem_axi_bvalid     ),
        .m00_axi_bready     (mem_axi_bready     ),
        .m00_axi_arid       (mem_axi_arid       ),
        .m00_axi_araddr     (mem_axi_araddr     ),
        .m00_axi_arlen      (mem_axi_arlen      ),
        .m00_axi_arsize     (mem_axi_arsize     ),
        .m00_axi_arburst    (mem_axi_arburst    ),
        .m00_axi_arlock     (mem_axi_arlock     ),
        .m00_axi_arcache    (mem_axi_arcache    ),
        .m00_axi_arprot     (mem_axi_arprot     ),
        .m00_axi_arqos      (mem_axi_arqos      ),
        .m00_axi_arregion   (mem_axi_arregion   ),
        .m00_axi_aruser     (mem_axi_aruser     ),
        .m00_axi_arvalid    (mem_axi_arvalid    ),
        .m00_axi_arready    (mem_axi_arready    ),
        .m00_axi_rid        (mem_axi_rid        ),
        .m00_axi_rdata      (mem_axi_rdata      ),
        .m00_axi_rresp      (mem_axi_rresp      ),
        .m00_axi_rlast      (mem_axi_rlast      ),
        .m00_axi_ruser      (mem_axi_ruser      ),
        .m00_axi_rvalid     (mem_axi_rvalid     ),
        .m00_axi_rready     (mem_axi_rready     )
    );

    ysyx_040991_axi_clint 
    #(
        .ysyx_040991_DATA_WIDTH        (64       ),
        .ADDR_WIDTH        (32       ),
        .STRB_WIDTH        (8        ),
        .ID_WIDTH          (4        )
    )
    u_axi_clint(
        .clk             (clock             ),
        .rst             (reset             ),
        
        .s_axi_awid     (clint_axi_awid     ),
        .s_axi_awaddr   (clint_axi_awaddr   ),
        .s_axi_awlen    (clint_axi_awlen    ),
        .s_axi_awsize   (clint_axi_awsize   ),
        .s_axi_awburst  (clint_axi_awburst  ),
        .s_axi_awlock   (clint_axi_awlock   ),
        .s_axi_awcache  (clint_axi_awcache  ),
        .s_axi_awprot   (clint_axi_awprot   ),
        .s_axi_awqos    (clint_axi_awqos    ),
        .s_axi_awregion (clint_axi_awregion ),
        .s_axi_awuser   (clint_axi_awuser   ),
        .s_axi_awvalid  (clint_axi_awvalid  ),
        .s_axi_awready  (clint_axi_awready  ),
        .s_axi_wdata    (clint_axi_wdata    ),
        .s_axi_wstrb    (clint_axi_wstrb    ),
        .s_axi_wlast    (clint_axi_wlast    ),
        .s_axi_wuser    (clint_axi_wuser    ),
        .s_axi_wvalid   (clint_axi_wvalid   ),
        .s_axi_wready   (clint_axi_wready   ),
        .s_axi_bid      (clint_axi_bid      ),
        .s_axi_bresp    (clint_axi_bresp    ),
        .s_axi_buser    (clint_axi_buser    ),
        .s_axi_bvalid   (clint_axi_bvalid   ),
        .s_axi_bready   (clint_axi_bready   ),
        .s_axi_arid     (clint_axi_arid     ),
        .s_axi_araddr   (clint_axi_araddr   ),
        .s_axi_arlen    (clint_axi_arlen    ),
        .s_axi_arsize   (clint_axi_arsize   ),
        .s_axi_arburst  (clint_axi_arburst  ),
        .s_axi_arlock   (clint_axi_arlock   ),
        .s_axi_arcache  (clint_axi_arcache  ),
        .s_axi_arprot   (clint_axi_arprot   ),
        .s_axi_arqos    (clint_axi_arqos    ),
        .s_axi_arregion (clint_axi_arregion ),
        .s_axi_aruser   (clint_axi_aruser   ),
        .s_axi_arvalid  (clint_axi_arvalid  ),
        .s_axi_arready  (clint_axi_arready  ),
        .s_axi_rid      (clint_axi_rid      ),
        .s_axi_rdata    (clint_axi_rdata    ),
        .s_axi_rresp    (clint_axi_rresp    ),
        .s_axi_rlast    (clint_axi_rlast    ),
        .s_axi_ruser    (clint_axi_ruser    ),
        .s_axi_rvalid   (clint_axi_rvalid   ),
        .s_axi_rready   (clint_axi_rready   ),
        .timer_interrupt (timer_interrupt )
    );

endmodule
